代码之家  ›  专栏  ›  技术社区  ›  John

使物体在内部与另一物体相等

  •  0
  • John  · 技术社区  · 7 年前

    给定对象 A ,类型 MyObj ,和一个对象 B 迈奥比 迈奥比 参数并将当前对象设置为该对象。

    class MyObj 
    { 
        /*lots of fields*/
        public void SetEqual(MyObj other)
        {
            /* What should go here, so that when the method ends, this is equal to other in every field OR they are ReferenceEqual */
        }
    }
    

    目前我能想到的唯一方法是反射或手动设置每个值相等(不利于重构)-没有办法使当前对象成为另一个对象的别名(类似于 ref 关键词),因为显而易见的原因,要获得引用平等,所以价值平等是唯一的方法——有没有更有效、更务实的方法来做到这一点(反思是缓慢的;手册是笨重的),还是没有?

    2 回复  |  直到 7 年前
        1
  •  1
  •   Matthew K. Crandall    7 年前

    嗯,我想到了四种方法。 (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

    (在注释后添加此方法) 这个方法实际上只是语法上的糖分,最好还是留作赋值运算符,但是如果出于某种原因确实需要一个方法,那么 能够

    正面 :

    缺点 :

    • 当一个简单的 a = b ; 就足够了。。。

    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.
    
        2
  •  0
  •   InBetween    7 年前

    引用等于 我的意思是。

    相同的 对象(*)。两个对象的引用不相等,因为这是荒谬的;如果它们是两个不同的对象,那么根据定义它们永远不可能是相等的。

    现在,你的方法是什么 SetEqual

    (*)注意,根据该定义,值类型永远不能引用任何相等的值,因为值类型变量不存储引用。