【问题标题】:Abstract generic classes taking type parameters that are themselves derived from that class抽象泛型类采用本身派生自该类的类型参数
【发布时间】:2011-03-20 20:08:17
【问题描述】:

你认为创建一个抽象的泛型类是可以接受的还是不好的做法?

这允许抽象泛型类操作派生类的实例,特别是能够根据需要创建派生类的 new() 实例,并有助于避免在派生自它的具体类中重复代码.

如果“不好”,你更喜欢用什么替代方法来处理这种情况,你会如何构造下面的代码?

例如:-

    // We pass both the wrapped class and the wrapping class as type parameters 
    // to the generic class allowing it to create instances of either as necessary.

    public abstract class CoolClass<T, U>
        where U : CoolClass<T, U>, new()
    {
        public T Value { get; private set; }
        protected CoolClass() { }
        public CoolClass(T value) { Value = value; }
        public static implicit operator CoolClass<T, U>(T val)
        {
            // since we know the derived type and that its new(), we can
            // new up an instance of it (which we couldn't do as an abstract class)
            return new U() { Value = val};
        }
        public static implicit operator T(CoolClass<T, U> obj)
        {
            return obj.Value;
        }
    }

还有一个额外的问题:为什么这些隐式运算符中的一个有效而另一个无效?

例如

    public class CoolInt : CoolClass<int, CoolInt>
    {
        public CoolInt() {  }
        public CoolInt(int val) (val) { }
    }

                                    // Why does this not work
        CoolInt x = 5;
                                    // when this works
        CoolInt x2 = (CoolInt)5;    
                                    // and this works
        int j = x;

【问题讨论】:

    标签: generics abstract-class


    【解决方案1】:

    这有点主观,但我不是隐式演员表的忠实粉丝。当您使用它们时,代码经常会产生误导,有时如果它是由隐式强制转换引起的,则很难找到错误。如果您的课程仅设计用于使用它们,那么我不会以这种方式使用它。

    为什么这些隐式运算符中的一个有效而另一个无效?

    因为您定义了来自CoolClass&lt;T, U&gt; 的转换,而不是来自CoolInt。它们是不同的类型。如果您在 CoolInt 实现中使用此方法,它将起作用:

    public static implicit operator CoolInt(int val)
    

    关于泛型的使用

    如果您需要创建具有许多类的复杂继承层次结构(例如,引入新的抽象级别可能会很棘手),泛型的这种使用会对您的架构造成限制。但这真的取决于你需要什么。我实际上在其中一个项目中使用了这种技术来避免代码重复。如果您的 CoolClass 克服 new() 限制,您也可以将委托 Func&lt;U&gt; 传递给构造函数:)

    【讨论】:

    • 关于泛型类型参数的问题呢? (我应该将隐含的问题放在一个单独的问题中,因为这会分散主要问题的注意力。)
    • +1 但我不同意隐式转换的不赞成。如果不进行静态分析或认真使用调试器,就没有无法发现的代码;作为一名开发人员,我希望必须这样做才能分析更复杂的代码。复杂不等于坏。在这种情况下,它使类的消费者更有能力做他们想做的事。
    【解决方案2】:

    这是 C++ 中的一种常见(而且很好!)模式,看起来有点像这样:

    template<typename T> class Base {
        void foo() { T::foo(); /* Call the derived implementation*/ }
    };
    class Derived : public Base<Derived> {
        void foo() { /* do something*/ }
    };
    

    我们将它用于静态多态/继承,以及其他。

    但是,在 .NET 中,泛型参数在运行时并且还有反射,我不完全确定好处在哪里。我的意思是,派生类型很有用,但你必须问自己——有什么用,它与直接继承有何不同?

    【讨论】:

    • +1 - 但我真正希望在 C# 中能够做的是奇怪的递归模板 - 遗憾的是 C# 没有太多可谈论的预编译器!有趣的是,您提到了事物的反射方面-我开发了一个组件,该组件动态编译了派生类或接口的实现,以构建诸如横切之类的东西。虽然我很欣赏在 .Net 中可以相对轻松地完成这类事情,但这表示这是一个陡峭的学习曲线。
    猜你喜欢
    • 2011-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-11
    • 2021-08-04
    • 2011-01-11
    • 1970-01-01
    • 2013-06-30
    相关资源
    最近更新 更多