Heim  >  Artikel  >  Backend-Entwicklung  >  3 Gedanken zur C#-Referenz im Detail

3 Gedanken zur C#-Referenz im Detail

黄舟
黄舟Original
2017-03-04 10:53:591731Durchsuche

1 Wenn die Werte gleich sind, sind die Objekte standardmäßig gleich?

Was sind die Standardregeln zur Bestimmung der Existenz eines Referenztyps im .net-Container? Bestimmen Sie, ob die Zeigerwerte gleich sind.

        private static List<int> list;        static void Main(string[] args)
        {            //新建实例instance1
            MyObject instance1 = new MyObject();
            instance1.Value = 10;            //新建list
            List<MyObject> list = new List<MyObject>();            //引用实例instance1
            list.Add(instance1);            //新建实例:instance2
            MyObject instance2 = new MyObject();            //赋值为instance1.Value
            instance2.Value = instance1.Value;       
        }
    }

Verwendete Modellklasse:

            public class MyObject
            {
                public int Value { get; set; }
            }

Führen Sie einen Test unten durch:

            //即便Value相等,instance2与instance1的内存地址不相等!
            bool isExistence1 = list.Contains(instance2);            //isExistence1 : false;

Dieses Testergebnis ist falsch , weil sie zeigen auf unterschiedliche Speicheradressen, obwohl die Werte gleich sind, ist dies der Fall bei „Gleiche Werte, aber nicht gleiche Objekte“ .
 
Wenn ein Referenztyp anhand eines seiner Attributwerte feststellen möchte, ob er gleich ist, muss er die IEquatable-Schnittstelle implementieren!
Wenn Sie weiterhin anhand der Gleichheit der Werte sehen möchten, ob Objekte gleich sind, lesen Sie bitte den Artikel: C#-Container, Schnittstellenklasse, Leistung

2 Referenzfalle?

 Ein Objekt verweist auf ein anderes Objekt. Wenn sich eines ändert, ändert sich auch das andere . Wenn beispielsweise zwei Wörterbücher zusammengeführt werden, ist das Zusammenführungsergebnis korrekt, das ursprüngliche Objekt wird jedoch versehentlich geändert.

Hier ist ein Beispiel:

            var dict1 = new Dictionary<string, List<string>>();
            dict1.Add("qaz",new List<string>(){"100"});//含有qaz键
            dict1.Add("wsx",new List<string>(){"13"});            
            var dict2 = new Dictionary<string, List<string>>();
            dict2.Add("qaz", new List<string>() { "11" });//也含有qaz键
            dict2.Add("edc", new List<string>() { "17" });            //合并2个字典到dict            
            var dictCombine = new Dictionary<string, List<string>>();
            foreach (var ele in dict1) //拿到dict1
            {
               dictCombine .Add(ele.Key,ele.Value); 
            }

            foreach (var ele in dict2) //拿到dict2
            {                if(dictCombine.ContainsKey(ele.Key))//检查重复
                   dictCombine [ele.Key].AddRange(ele.Value); 
                else
                {
                    dictCombine .Add(ele.Key,ele.Value); 
                }
            }

Das Ergebnis von dictCombine ist korrekt, {"qaz", "100" und "11"}, { "wsx", "13"}, {"edc", "17"}
Aber was ist mit dem Ergebnis von dict1? Wurde geändert! dict1 wurde unerwartet zu {"qaz", "100" und "11"}, {"wsx", "13"}. Korrekte Zusammenführung, dict1 sollte nicht geändert werden!

Analyse des Grundes

dictCombine fügt zunächst den Schlüsselwert von dict1 hinzu, dh der Schlüsselwert von dictCombine verweist alle auf den Schlüsselwert von dict1; Als nächstes ermitteln Sie beim Zusammenführen von dictCombine zunächst, ob dictCombine den Schlüssel von dictCombine enthält. Der Wert bezieht sich auf dasselbe Objekt, das heißt, dieser Wert wird zum Schlüssel von dictCombine hinzugefügt. Überprüfung, ob die Referenzen dictCombine[ele.Key] und dict1[ele.Key] gleich sind:

bool flag = object.ReferenceEquals(dictCombine[ele.Key], dict1[ele.Key]);//true

Richtige Lösung

dictCombine[ele.Key] vermeiden und dict1 [ele.Key] Referenzgleichheit! ! !

Dictionary<string, List<string>> dict = new Dictionary<string, List<string>>();            
//先把键都合并到dictCombine中,值都是新创建的
            foreach (var key in dict1.Keys)
            {                if (!dictCombine.ContainsKey(key))
                    dictCombine.Add(key, new List<string>());
            }            foreach (var key in dict2.Keys)
            {                if (!dictCombine.ContainsKey(key))
                    dictCombine.Add(key, new List<string>());
            }     //分别将值添加进去
            foreach (var ele in dict1)
            {
                dictCombine[ele.Key].AddRange(ele.Value);
            }            foreach (var ele in dict2)
            {
                dictCombine[ele.Key].AddRange(ele.Value);
            }

Das dictCombine-Zusammenführungsergebnis ist korrekt und weder dict1 noch dict2 haben sich geändert!

Zusammenfassung
  Die Verwendung der Referenzgleichheit bringt viele Vorteile mit sich, beispielsweise durch die Referenzübertragung zwischen Funktionen. Bei unsachgemäßer Verwendung wird es uns jedoch auch unnötigen Ärger bereiten.​

3 Falsche Referenz zerstört die Kapselung?
 
Wenn das private Feld in der gekapselten Klasse als Rückgabewert der Schnittstellenmethode verwendet wird, zerstört dieser Ansatz die Kapselung von die Klasse, insbesondere ein Problem, das leicht übersehen wird. Wenn Sie dieses Problem ignorieren, können unerklärliche Probleme auftreten.
 
 Wie im folgenden Code gezeigt,
 

public class TestPrivateEncapsulate
{
    private List<object> _refObjs;

    public List<object> GetRefObjs()
    {
        _refObjs = new List<object>();        ...
        ...
       //其他逻辑处理计算出来的_refObjs={1,4,2};    
        return _refObjs; //返回私有字段
    }

    public object GetSumByIterRefObjs()
    {        if (_refObjs == null)            return null;
        foreach (var item in _refObjs)
        {            ...//处理逻辑
        }
    }  
}

 Nun erstellen wir zunächst mit der Klasse TestPrivateEncapsulate, die wir gerade geschrieben haben, eine Instanz,

TestPrivateEncapsulate test = new TestPrivateEncapsulate();

Dann rufen Sie auf:

List<object> wantedObjs = test.GetRefObjs();

Die erwarteten zurückgegebenen WantedObjs sollten 3 Elemente vom Typ Integer haben, 1, 4, 2.

Weiter:

List<object> sol = wantedObjs; //我们将sol指向wantedObjssol.Add(5); //加入元素5

Wenn wir zurückgehen und berechnen wollen, ist die ursprüngliche Summe der Elemente von WantObjs:

test.GetSum();

Wir haben versehentlich 12 erhalten, nicht was Wir haben von 7 erwartet. Warum ist das so?

Nach sorgfältiger Analyse haben wir festgestellt, dass wir nach dem Aufruf von sol.Add(5) auf dem Client indirekt die Variable in TestPrivateEncapsulate geändert haben: _refObjs, die von {1,4,2} in {1, 4 ,2,5}.

 Private Variablen wurden auf der Clientseite geändert! Dies ist der Nebeneffekt der Schnittstelle, die private Variablen zurückgibt!

 Richtige Lösung:

    // 将原来的公有变为私有
    private List<object> getRefObjs()
    {
        _refObjs = new List<object>();        ...
        ...
       //其他逻辑处理计算出来的_refObjs={1,4,2};    
        return _refObjs; //返回私有字段
    }

    //只带只读的属性
    public RefObjs
    {
        get
         {
            getRefObjs();            return _refObjs;
         }
    }

Legen Sie ein öffentliches Feld mit nur schreibgeschützten Attributen fest und ändern Sie die ursprüngliche öffentliche Methode GetRefObjs in die private Methode getRefObjs Auf diese Weise ist es unmöglich, private Felder auf der Clientseite zu ändern!

Zusammenfassung
Die Attributwerte der Objekte sind alle gleich, aber die Objektreferenzen sind nicht unbedingt gleich.
Zwei oder mehr Objekte verweisen auf ein Objekt. Wenn dieses Objekt geändert wird, sind die Attributwerte aller Referrer gleich ebenfalls geändert;
Member return Gekapselte Referenzvariablen zerstören die Kapselung.

Das Obige ist die detaillierte Einführung der drei Gedanken der C#-Referenz. Weitere verwandte Inhalte finden Sie auf der chinesischen PHP-Website (www.php.cn)!


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn