本文摘自PHP中文网,作者黄舟,侵删。
1 值相等,对象便默认相等?
.net 容器中判断某个引用类型存在的默认规则是什么? 判断指针值是否相等。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | private static List< int > list;
static void Main( string [] args)
{
MyObject instance1 = new MyObject();
instance1.Value = 10;
List<MyObject> list = new List<MyObject>();
list.Add(instance1);
MyObject instance2 = new MyObject();
instance2.Value = instance1.Value;
}
}
|
用到的Model类:
1 2 3 4 | public class MyObject
{
public int Value { get ; set ; }
}
|
下面做1个测试:
1 2 | bool isExistence1 = list.Contains(instance2);
|
这个测试结果是false,因为它们指向不同的内存地址,尽管值相等,这便是“值相等,对象不相等”的情况。
引用类型若是想根据其中的某个属性值判断是否相等,那么需要实现IEquatable接口!
若想继续看 根据值是否相等 判断对象是否相等,请参考文章:C# 容器,接口类,性能
2 引用陷阱?
一个对象引用另一个对象,某一个改变,另一个便改变。例如,合并两个字典,合并结果是对的,但是却意外改变了原对象。
在这里举一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | var dict1 = new Dictionary< string , List< string >>();
dict1.Add( "qaz" , new List< string >(){ "100" });
dict1.Add( "wsx" , new List< string >(){ "13" }); var dict2 = new Dictionary< string , List< string >>();
dict2.Add( "qaz" , new List< string >() { "11" });
dict2.Add( "edc" , new List< string >() { "17" });
var dictCombine = new Dictionary< string , List< string >>();
foreach ( var ele in dict1)
{
dictCombine .Add(ele.Key,ele.Value);
}
foreach ( var ele in dict2)
{ if (dictCombine.ContainsKey(ele.Key))
dictCombine [ele.Key].AddRange(ele.Value);
else
{
dictCombine .Add(ele.Key,ele.Value);
}
}
|
dictCombine的结果正确,{“qaz”, “100”和”11”}, {“wsx”,”13”},{“edc”,”17”}
但是dict1的结果怎么样? 被改变了! dict1意外变为了 {“qaz”, “100”和”11”}, {“wsx”,”13”}。 正确的合并,不应该改变dict1!
分析原因
dictCombine首先添加了dict1的键值,也就是dictCombine的键值都引用了dict1的键值; 接下来,再合并dict2时,首先判断dictCombine中是否包含了dict2的键,如果包含,则再往dictCombine的键值中添加, 值又引用了同一个对象,也就是在dict1的键中添加了这个值。dictCombine[ele.Key]和dict1[ele.Key]引用是否相等的验证:
1 | bool flag = object .ReferenceEquals(dictCombine[ele.Key], dict1[ele.Key]);
|
正解
避免dictCombine[ele.Key]和dict1[ele.Key]引用相等!!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | Dictionary< string , List< string >> dict = new Dictionary< string , List< string >>();
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);
}
|
dictCombine合并结果是正确的,并且dict1,dict2都未改变!
总结
利用引用相等,带来了很多好处,比如函数间的引用传值(by reference)。但是,如果运用不当,也会给我们带来一些不必要的麻烦。
3 引用不当破坏封装?
如果将封装的类内私有字段作为接口方法的返回值,这种做法会破坏类的封装,是特别容易忽视的一个问题。如果忽视这个问题,可能会出现莫名其妙的问题。
如下面的代码所示,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public class TestPrivateEncapsulate
{
private List< object > _refObjs;
public List< object > GetRefObjs()
{
_refObjs = new List< object >(); ...
...
return _refObjs;
}
public object GetSumByIterRefObjs()
{ if (_refObjs == null ) return null ;
foreach ( var item in _refObjs)
{ ...
}
}
}
|
现在使用刚才写的类TestPrivateEncapsulate,我们先创建一个实例,
1 | TestPrivateEncapsulate test = new TestPrivateEncapsulate();
|
然后调用:
1 | List< object > wantedObjs = test.GetRefObjs();
|
返回的预期wantedObjs应该有3个整形类型的元素,1,4,2。
继续:
1 | List< object > sol = wantedObjs;
|
等我们想回过头来计算,原来wantedObjs的元素求和:
我们意外得到了12,而不是预想中的7。这是为什么呢?
仔细分析后发现,我们在客户端调用,sol.Add(5)后,间接的修改了TestPrivateEncapsulate内变量:_refObjs,它由{1,4,2}修改为了{1,4,2,5}。
私有变量在客户端被修改了!这便是接口返回私有变量带来的副作用!
正解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | private List< object > getRefObjs()
{
_refObjs = new List< object >(); ...
...
return _refObjs;
}
public RefObjs
{
get
{
getRefObjs();
return _refObjs;
}
}
|
设置一个公有字段,仅带有只读属性,将原来的公有方法GetRefObjs变为私有方法getRefObjs,这样在客户端是不可能修改私有字段的!
总结
对象的属性值都等,但对象引用不一定相等;
两个或多个对象都引用某个对象,若这个对象被修改,则所有引用者属性值也被修改;
成员返回封装的引用变量,会破坏封装。
以上就是.NET框架-引用陷阱的代码实例分享的详细内容!
相关阅读 >>
.NET框架-详解winform技术中组件被容器引用陷阱
.NET框架- in ,out, ref , paras使用的代码总结
.NET框架-微软给出的c#编程风格代码实例
.NET框架-array的详细介绍
.NET框架-clone如何由浅变深的示例代码详解
.NET框架-集合相关所有类的思维导图分享
.NET框架-try-parse和tester-doer的使用区别
.NET框架-集合和linq中的“分组”技术代码详解
.NET框架-xml.serialization的思维导图分享
.NET框架-clone详请介绍
更多相关阅读请进入《.NET框架》频道 >>
清华大学出版社
作者:[美]克里斯琴·内格尔(Christian Nagel)著。出版时间:2019年3月。
转载请注明出处:木庄网络博客 » .NET框架-引用陷阱的代码实例分享