【问题标题】:Class extending - best practice/best solution类扩展 - 最佳实践/最佳解决方案
【发布时间】:2011-04-08 11:25:05
【问题描述】:

首先要注意 - 我知道委托和装饰模式!

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

问题来了……

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

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

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

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

这里是基于Class1创建Class2的扩展方法的完整代码

    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;
    }

(顺便说一句,这种方法可能无法理想地实现我的解决方案,因此也将不胜感激)

提前致谢!

【问题讨论】:

    标签: c# oop reflection properties attributes


    【解决方案1】:

    小问题似乎不是什么大问题。也许我误解了这个问题,但假设您只是派生一个子类,则没有理由重新定义属性或其关联的 getter/setter。

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

    您的反射初始化程序很有趣,但如果您打算做很多这样的事情,那么您可能会考虑在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) {}
    }
    

    【讨论】:

    • 在创建属性 new 时,我必须重新定义 getter 和 setter,而复制构造函数只是编写大量行的另一种变体,例如 property = property
    • 感谢 AutoMapper - 这很有趣,但我不确定是否要导入第三方解决方案来解决这个小问题。
    【解决方案2】:

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

    我知道一种方法可以做到这一点,但根据爬取属性的方式,它可能不起作用。

    // 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。

    【讨论】:

    • 我一定会尝试你的接口方法,如果属性确实被继承爬取,它看起来很棒。
    • 让 Class2 成为包装器并不是我要寻找的,因为在每个属性的主体中我都必须写 return class1.&lt;propertyname&gt;class1.&lt;propertyname&gt; = value,我想避免这种情况——这意味着太多没用了编码。我的方法看起来很相似,但我只为那些需要属性的属性定义了主体——这在某些情况下会有所不同。无论如何,如果界面方法有效,我更大的问题可能只是一个理论上的问题。谢谢!
    猜你喜欢
    • 2011-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-24
    • 1970-01-01
    • 1970-01-01
    • 2011-11-05
    • 1970-01-01
    相关资源
    最近更新 更多