课程延伸 - 最佳实践/最佳解决方案

时间:2010-09-05 15:28:17

标签: c# oop reflection properties attributes

首先要注意的是 - 我知道代表和装饰图案!

第二 - 我正在使用C#.NET 4.0,所以如果你想出一个特定于它的解决方案,那很好。但是,如果解决方案适用于任何OOP语言和平台,那就太棒了。

这里的问题是......

我有一个部分类(让我们命名为Class1),我无法修改。因此,我可以扩展它或/并继承它。这个类为我提供了一个完美的数据模型,我唯一需要的是为它的属性添加一些属性(用于验证,在MVC中定义标签文本值等等 - 现在,我不需要答案,比如'你能做什么你需要没有属性',这不是我的问题)。

将另一个类用作数据模型不是问题,因此我可以创建Class2 : Class1并使用Class2作为模型。需要属性的属性将定义为public new <type> <propertyname>。这将限制我仅重写需要属性的属性,而保留所有其他属性。

较小的问题是我没有重新定义属性的getter和setter,因为它们包含的所有内容都是return base.<propertyname>base.<propertyname> = value,如果有很多这样的属性,这意味着很多“愚蠢”的编码。有没有办法避免这种情况?

更大的问题是我必须使用Class2实例对我的Class1进行参数化,并为我拥有的每个属性制作类似class2.<propertyname> = class1.<propertyname>的内容 - 过多的“愚蠢”编码。我可以使用反射来避免它 - 在Class1中找到包含公共getter和setter的所有属性,并在循环中调用prop.SetValue(child, prop.GetValue(parent, null), null);。这为简单的情况提供了一个通用函数,这很好,因为我大多数都有简单的模型 - 很多属性与公共getter和setter没有body和另一个逻辑。但我想要更通用的解决方案,我不喜欢反思。有什么想法吗?

以下是基于Class2

创建Class1的扩展方法的完整代码
    public static Child ToExtendedChild<Parent, Child>(this Parent parent)
        where Child : Parent, new()
    {
        Child child = new Child();

        var props = typeof(Parent).GetProperties().Where(p => p.GetAccessors().Count() >= 2);

        foreach (var prop in props)
        {
            prop.SetValue(child, prop.GetValue(parent, null), null);
        }

        return child;
    }

(顺便说一句,这种方法可能无法实现我的解决方案,所以任何更正也会受到赞赏)

提前致谢!

2 个答案:

答案 0 :(得分:2)

较小的问题似乎不是什么大问题。也许我误解了这个问题,但假设你只是在推导一个子类,那么就没有理由重新定义属性或它们相关的getter / setter。

更大的问题可能会使用更简单的东西来解决。对很多对象初始化使用反射似乎有点贵。如果您正在处理主要是大包或属性的类,也许您应该在任何给定情况下都需要访问所有这些属性。你提到MVC和验证,你正在验证的控制器方法中使用的整个模型是什么?如果没有,为什么不考虑使用只暴露那个方法所需的那些部分的viewmodel?

你的反射初始化器很有意思,但是如果你要做很多这样的话,你可以考虑用Automapper投入一点时间。否则,可以考虑从通用解决方案转向仅解决手头问题的方法,即将属性从对象实例映射到派生对象的另一个实例。也许你可以在父类中创建一个复制构造函数并在派生类中使用它?

public class Foo {
    public string PropOne { get; set; }
    public string PropTwo { get; set; }

    public Foo(string propOne, string propTwo) {
        PropOne = propOne;
        PropTwo = propTwo;
    }

    public Foo(Foo foo) {
        PropOne = foo.PropOne;
        PropTwo = foo.PropTwo;
    }
}

public class Pho : Foo {
    // if you have additional properties then handle them here
    // and let the base class take care of the rest.
    public string PropThree { get; set; }
    public Pho(string propOne, string propTwo, string propThree) 
        : base(propOne, propTwo) {
        PropThree = propThree; 
    }
    public Pho(Pho pho) : base(pho) {
        PropThree = pho.PropThree;
    }
    // otherwise you can just rely on a copy constructor
    // to handle the initialization.
    public Pho(Foo foo) : base(foo) {}
}

答案 1 :(得分:1)

我假设部分类是生成的代码,它在你的场景中最有意义。

我知道有一种方法可以执行此操作,但根据属性的爬网方式,它可能无效。

// Generated Code
public partial Class1
{
  public string Foo { get { ... } }
}

// Your Code
public interface IClass1
{
  [MyAttribute]
  public string Foo { get; }
}

public partial Class1 : IClass1
{
}

如果有人通过使用带继承的GetCustomAttributes来查看属性,那么我认为他们会得到这个属性。

顺便说一句,每当我看到生成的代码没有虚拟属性时,它就会让我内心哭泣。

为了解决您的更大问题,为什么不让Class2成为Class1的包装器。您可以在构造函数中为Class2提供Class1实例,而不是复制所有属性,而是将其存储在本地并使所有属性成为传递。这意味着一些手动编码,但是如果你手动构建一个Class2并想用一堆属性来装饰它,那么,无论如何你都在编写Class2。