C# 如何使用反射来简化构造函数&;比较?

C# 如何使用反射来简化构造函数&;比较?,c#,reflection,refactoring,C#,Reflection,Refactoring,我讨厌有一大堆“左/右”的方法。每次添加或删除属性时,我都必须修复每个方法。代码本身看起来。。。错 public Foo(Foo other) { this.Bar = other.Bar; this.Baz = other.Baz; this.Lur = other.Lur; this.Qux = other.Qux; this.Xyzzy= other.Xyzzy; } 实际上,这只是一个展开的循环,循环遍历属性,在对象之间复制它们。那么,为什么不诚

我讨厌有一大堆“左/右”的方法。每次添加或删除属性时,我都必须修复每个方法。代码本身看起来。。。错

public Foo(Foo other)
{
    this.Bar = other.Bar;
    this.Baz = other.Baz;
    this.Lur = other.Lur;
    this.Qux = other.Qux;
    this.Xyzzy= other.Xyzzy;
}
实际上,这只是一个展开的循环,循环遍历属性,在对象之间复制它们。那么,为什么不诚实地面对这个事实呢?反思救援

public Foo(IFoo other)
{
    foreach (var property in typeof(IFoo).GetProperties())
    {
        property.SetValue(this, property.GetValue(other, null), null);
    }
}
我可能只是想把我从Lua学来的一个范例强加于C#,但这个特殊的例子对我来说并不太难闻。从这里开始,我开始做一些更复杂的事情,这些事情对字段的顺序很敏感。例如,我没有使用一堆几乎相同的
if
语句从字段组成字符串,而是按照所需的顺序对它们进行迭代:

public override string ToString()
{
    var toJoin = new List<string>();
    foreach (var property in tostringFields)
    {
        object value = property.GetValue(this, null);
        if (value != null)
            toJoin.Add(value.ToString());
    }
    return string.Join(" ", toJoin.ToArray());
}
private static readonly PropertyInfo[] tostringFields =
{
    typeof(IFoo).GetProperty("Bar"),
    typeof(IFoo).GetProperty("Baz"),
    typeof(IFoo).GetProperty("Lur"),
    typeof(IFoo).GetProperty("Qux"),
    typeof(IFoo).GetProperty("Xyzzy"),
};
public重写字符串ToString()
{
var toJoin=新列表();
foreach(ToString字段中的var属性)
{
object value=property.GetValue(这个,null);
if(值!=null)
添加(value.ToString());
}
返回字符串.Join(“,toJoin.ToArray());
}
私有静态只读属性Info[]ToString字段=
{
typeof(IFoo).GetProperty(“Bar”),
typeof(IFoo).GetProperty(“Baz”),
typeof(IFoo).GetProperty(“Lur”),
typeof(IFoo).GetProperty(“Qux”),
typeof(IFoo).GetProperty(“xyzy”),
};
所以现在我有了我想要的可写性,但我仍然有一堆代码镜像我感兴趣的每个属性(我也在为CompareTo做这件事,以不同的顺序使用不同的属性集)。比这更糟糕的是失去了强大的打字能力。这真的开始有味道了


那么,如何使用每个属性的属性来定义顺序呢?我开始沿着这条路走,确实效果很好,但它只是让整个事情看起来臃肿。它在语义上非常有效,但我总是担心仅仅因为高级功能“整洁”就使用它们。以这种方式使用反射是否过火了?对于我所缺少的左/右代码问题,还有其他解决方案吗?

使用反射本身并不坏,但是您会受到性能的影响,特别是如果您递归地这样做的话

我也不喜欢硬编码的拷贝构造函数,因为开发人员在向类添加新属性时忘记了更新它们

还有其他方法可以实现您想要的功能,包括Marc Gravels,或者如果您想学习一些IL和操作码,您可以使用System.Reflection.Emit甚至更高版本

下面是一个使用超属性描述符的示例,您可以根据自己的需要进行定制:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using Hyper.ComponentModel;
namespace Test {
    class Person {
        public int Id { get; set; }
        public string Name { get; set; }
    }
    class Program {
        static void Main() {
            HyperTypeDescriptionProvider.Add(typeof(Person));
            var properties = new Dictionary<string, object> { { "Id", 10 }, { "Name", "Fred Flintstone" } };
            Person person = new Person();
            DynamicUpdate(person, properties);
            Console.WriteLine("Id: {0}; Name: {1}", person.Id, person.Name);
            Console.ReadKey();
        }
        public static void DynamicUpdate<T>(T entity, Dictionary<string, object>  {
            foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(typeof(T)))
                if (properties.ContainsKey(propertyDescriptor.Name))
                    propertyDescriptor.SetValue(entity, properties[propertyDescriptor.Name]);
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用系统组件模型;
使用Hyper.component模型;
名称空间测试{
班主任{
公共int Id{get;set;}
公共字符串名称{get;set;}
}
班级计划{
静态void Main(){
HyperTypeDescriptionProvider.Add(typeof(Person));
var properties=新字典{{“Id”,10},{“Name”,“Fred Flintstone”};
Person=新人();
日期(人、财产);
WriteLine(“Id:{0};Name:{1}”,person.Id,person.Name);
Console.ReadKey();
}
公共静态void DynamicUpdate(T实体、字典{
foreach(TypeDescriptor.GetProperties(typeof(T))中的PropertyDescriptor PropertyDescriptor)
if(properties.ContainsKey(propertyDescriptor.Name))
SetValue(实体,属性[propertyDescriptor.Name]);
}
}
}
如果决定继续使用反射,可以通过如下方式缓存对GetProperties()的调用来降低性能影响:

public Foo(IFoo其他){
foreach(MyCacheProvider.GetProperties()中的var属性)
property.SetValue(this,property.GetValue(other,null),null);
}

IMHO,反射是C#的一个非常强大的功能,但它很可能导致代码膨胀,这会增加代码的学习曲线并降低可维护性。您更可能犯错误(一旦基本重构可能导致错误),并且更害怕更改任何属性的名称(如果你碰巧找到一个更好的名字)或诸如此类的东西


我个人有一个代码有类似的问题,我也有相同的想法,添加属性来维持秩序等等。但是我的团队(包括我)认为最好是浪费一些时间来改变设计,而不需要这样做。也许这个问题是由糟糕的设计造成的(好吧,这是我的情况,但我不能告诉你同样的情况).

我知道已经有了答案,但我想指出的是,有一个库结合了一些人讨论过的性能影响缓解策略

该库被调用,它从一个对象映射到另一个对象,并通过动态创建IL程序集来实现。这确保了除了第一次命中之外,您还可以获得更高的性能,并且您的代码将更加简单:

public Foo(Foo other)
{
    Mapper.Map(other, this);
}
这往往效果很好,而且还有一个额外的好处,那就是我不是这里的发明者,我是这里的粉丝

我做了一些性能测试,在第一次达到20毫秒(仍然相当快)后,它几乎接近于0。非常令人印象深刻


希望这对其他人有所帮助。

基本问题是您试图使用静态类型语言,就像使用动态类型语言一样

实际上不需要任何花哨的东西。如果您希望能够迭代属性,可以使用映射作为类中所有属性的后备存储

巧合的是,这正是VS project向导为您实现应用程序设置的方式。(请参阅System.Configuration.ApplicationSettingsBase)它也非常类似于lua

   public bool ConfirmSync {
        get {
            return ((bool)(this["ConfirmSync"]));
        }
        set {
            this["ConfirmSync"] = value;
        }
    }

我要添加一个语言标签来吸引更多的观众记住,在C#中,反射总是伴随着性能的提高。多久会出现一次
   public bool ConfirmSync {
        get {
            return ((bool)(this["ConfirmSync"]));
        }
        set {
            this["ConfirmSync"] = value;
        }
    }