嗯,我想到了四种方法。
(11/13/2018)编辑:增加方法4
:
-
-
显式控制哪些属性被拉倒(如果
要排除某些属性)
-
明确控制每个属性的深度和浅度
-
维修
-
当未来的开发人员(也许是你,也许不是)向类添加新的字段/属性时,很容易错过
class MyObj
{
public void SetEquals(MyObj other)
{
if (object.ReferenceEquals(this, other)) return; // We are equal by reference, so do nothing.
if (other == null) return; // Throw ArgumentException? Up to you.
this.Property1 = other.Property1;
this.Property2 = other.Property2;
this.Property3 = other.Property3;
// ...
}
}
方法2
:
正面
:
缺点
:
-
更快)
-
写起来很复杂,如果你几乎没有思考的经验
-
编写它以支持嵌套类型中嵌套类型的深层和浅层副本可能会成为一个固有的递归问题,因为复杂的属性系统支持对深层和浅层情况的粒度控制
using System.Reflection;
public static class OverrideObjectValues
{
private static Dictionary<Type, Tuple<PropertyInfo[], FieldInfo[]>> cachedLookup = new Dictionary<Type, Tuple<PropertyInfo[], FieldInfo[]>>
// Copies fields and properties from the right object into the left object.
// Could be extended to support attribute-level customization
// guiding this reflector on properties/fields to ignore,
// And whether to perform a deep or shallow copy of reference types
// for instance properties of types left and right.
public static void OverrideValues(object left, object right)
{
// They are equal by reference, we're done.
// This also handles the case that both left and right are null.
if (object.ReferenceEquals(left, right)) return;
// One or the other is null; we can't do this.
// Alternatively, throw an ArgumentException here?
if (left == null || right == null) return;
// The types mismatch; we can't do this.
// Alternatively, throw an ArgumentException here?
// Note: We could modify this to support the case where
// Left or Right inherits from the other, but that becomes
// more complex, and is beyond the scope of what
// you're asking for.
if (left.GetType() != right.GetType()) return;
Type leftType = left.GetType();
if (!cachedLookup.ContainsKey(leftType))
{
// Add type to cache
cachedLookup.Add(leftType, new Tuple<PropertyInfo[], FieldInfo[]>(leftType.GetProperties(), leftType.GetFields()));
}
// Iterate around each property, and copy-by-value from right into left.
// Do the same for each field, for the type we cached in the dictionary.
// You can add support to exclude properties/fields which are decorated
// with custom attributes. If you do support guiding by custom attributes,
// I'd exclude these types in the lookup/cache step in the dictionary before this point.
// You could even add support to differentiate between structs and classes,
// and do deep / shallow copies accordingly...
}
}
:
当您要覆盖实例时
A
属于
MyObject
B
我的对象
,您可以直接使用赋值运算符通过引用使它们相等。注意:这样做意味着它们是同一个实例,这意味着对
A.
反映在
自从
B
在内存中是同一个对象。
正面
-
最快的
-
将来最容易理解(假设您知道引用类型如何工作)
-
维修
就这么简单:
// Populate list of objects.
List<MyObj> objects = GetObjectsSomehow();
// Copy by reference object at index 4 over object at index 5.
objects[5] = objects[4];
如您所知,方法3的示例如下
对原始数据的深度复制/覆盖,而是通过引用使两者(在本例中,我将它们存储在一个列表中)相同。如果对象是不可变的,这尤其有用,因为这不会违反整个不可变原则。。。
方法4
(在注释后添加此方法)
这个方法实际上只是语法上的糖分,最好还是留作赋值运算符,但是如果出于某种原因确实需要一个方法,那么
能够
正面
:
缺点
:
public class CustomType
{
public string Name { get; set; }
public int ID { get; set; }
}
然后你可以有一个带有扩展方法的静态类。。。
public static class CopyUtilities
{
public static void MakeReferenceEqual<T>(this T left, ref T right) where T : class
{
if (object.ReferenceEquals(left, right)) return; // we're reference-equal, so be done.
right = left;
}
}
你可以这样使用:
CustomType a = new CustomType();
a.ID = 42;
a.Name = "Myself";
CustomType b = null;
a.MakeReferenceEqual(ref b);
// a.ID == b.ID
// a.Name == b.Name
// a == b, by reference.