简体   繁体   English

C# - 类型约束和限制,保持类型安全的任何解决方法?

[英]C# - Type Constraints and Limitations, any workaround to keep Type Safety?

I have a pretty common scenario about the limitations of Generic Type Constraint that would required another Generic to be defined. 我有一个非常常见的场景,关于通用类型约束的限制,需要定义另一个Generic。

It has already been discussed (Eric Lippert himself and others) but so far I haven't seen a general guideline or let's say a rule of thumb that can be applied when bumping into the following scenario: 它已经被讨论过了(Eric Lippert本人和其他人),但到目前为止我还没有看到一般指导方针,或者让我们说出一个可以在碰到下面的场景时应用的经验法则:

public abstract class Class<TProperty> : Class
     where TProperty : Property<>
// Sadly the line above cannot work, although the compiler could actually infer
// the Generic Type, since defining two class definitions like:
// Considering A & B two other well-defined classes
// Class<TA> where TA : A and 
// Class<TB> where TB : B is not allowed and well-understandable
{
    protected Class(TProperty property)
    {
        if (property != null)
        {
            this._property = property;
        }
        else
        {
            throw new ArgumentNullException(@"property");
        }
    }

    private readonly TProperty _property;
    public TProperty Property
    {
        get
        {
            return this._property;
        }
    }
}
public abstract class Property<TParentClass>
// Same remark goes here
    where TParentClass : Class<>
{
    protected Property(TParentClass parent)
    {
        if (parent != null)
        {
            this._parent = parent;
        }
        else
        {
            throw new ArgumentNullException(@"parent");
        }
    }

    private readonly TParentClass _parent;
    internal TParentClass Parent
    {
        get
        {
            return this._parent;
        }
    }
}

This is fine we still have some workarounds by using interfaces or making up new base classes just as follows: 这很好我们通过使用接口或组成新的基类仍然有一些解决方法,如下所示:

public abstract class Class
{
}
public abstract class Class<TProperty> : Class
    where TProperty : Property
{
    protected Class(TProperty property)
    {
        if (property != null)
        {
            this._property = property;
        }
        else
        {
            throw new ArgumentNullException(@"property");
        }
    }

    private readonly TProperty _property;
    public TProperty Property
    {
        get
        {
            return this._property;
        }
    }
}

public abstract class Property
{
}
public abstract class Property<TParentClass>
    where TParentClass : Class
{
    protected Property(TParentClass parent)
    {
        if (parent != null)
        {
            this._parent = parent;
        }
        else
        {
            throw new ArgumentNullException(@"parent");
        }
    }

    private readonly TParentClass _parent;
    internal TParentClass Parent
    {
        get
        {
            return this._parent;
        }
    }
}

This is fine but what happen if I want to add a new legitimate layer of inheritance? 这很好但是如果我想添加一个新的合法继承层会发生什么?

public abstract class InheritedClass<TInheritedProperty> : Class<TInheritedProperty>
// Damn it! I wanted to be more specific but I cannot have <> (and also <,>, <,,>, etc.)
// Cannot do that without declaring another public interface... sad
// Or another non generic base class
    where TInheritedProperty : Property
{
    // But this remark cannot work here... I would have needed a "real" type not InheritedProperty<>...
    // Yeah this is it: starting to bake the noodles
    protected InheritedClass(TInheritedProperty property)
        : base(property)
    {
    }
}

public abstract class InheritedProperty<TInheritedClass> : Property<TInheritedClass>
// Same goes here
    where TInheritedClass : Class
{
    protected InheritedProperty(TInheritedClass parent)
        : base(parent)
    {
    }
}

or even worse (with a code cannot obviously compile) and go really stupid with no real type safety: 甚至更糟糕(代码不能明显编译)并且真正愚蠢没有真正的类型安全:

public abstract class InheritedClass2<TInheritedProperty, TInheritedPropertyClass> : Class<TInheritedProperty>
    where TInheritedProperty : InheritedProperty2<TInheritedPropertyClass, TInheritedProperty>
{
    protected InheritedClass2(TInheritedProperty property)
        : base(property)
    {
    }
}

public abstract class InheritedProperty2<TInheritedClass, TInheritedClassProperty> : Property<TInheritedClass>
    where TInheritedClass : InheritedClass2<TInheritedClassProperty, TInheritedClass>
{
    protected InheritedProperty2(TInheritedClass parent)
        : base(parent)
    {
    }
}

At that point people would usually say no, the design should not that complicated... review your business requirement and just use massively interface with composition, inheritance is not only for saving up you to write some extra code and those classes should form some sort of families, alright they do form a kind-a family. 在那时人们通常会说不,设计不应该那么复杂......检查你的业务需求,只是使用大量的接口与组合,继承不仅是为了节省你写一些额外的代码,这些类应该形成某种类型家庭,好吧,他们确实形成了一个家庭。

Well, fair enough but this does not really solve the situation which I have to confess is over-exagerated but there are cases where it makes sense to have an inheritance with constraints and where those constraints have also constraints. 嗯,公平,但这并没有真正解决我必须承认过度夸大的情况,但有些情况下,有一个带约束的继承和那些约束也有约束的情况。

Yes those ones can drive you really crazy (eg recursive constraints) and make you pulling you hair out... but still there are situations where this can be handy, especially in regarding type-safety. 是的那些可以让你真正疯狂(例如递归约束)并让你拉扯你的头发...但仍然有这种情况可以派上用场,特别是在关于类型安全方面。

Anyway, in regards to those constraints is what the most suitable, general guideline to follow to get back on tracks, any other solutions than just using interfaces or choosing a type subset in the constructor? 无论如何,关于这些约束是什么是最合适的一般准则,以回到轨道,除了在构造函数中使用接口或选择类型子集之外的任何其他解决方案?

Would this work for you? 这对你有用吗?

public abstract class Class<TProperty, T>
   where TProperty : Property<T>
{

}

It's not as powerful as type constructors, because the T in Property<T> cannot vary, but it doesn't seem like you're looking for that kind of flexibility anyway. 这并不是因为类型构造强大,因为TProperty<T>无法改变,但它似乎并不像你要找的那种灵活性反正。

I've encountered similar issues with trying to ultra-template a quite complicated application model in C# before. 我曾经遇到类似的问题,试图在C#中对一个相当复杂的应用程序模型进行超模板化。 I came to the conclusion that the compiler can only do so much though. 我得出结论,编译器只能这么做。

I'd only be worried about setting up quite complicated type constraints if you specifically need to do something with the type argument. 如果您特别需要对type参数执行某些操作,我只会担心设置相当复杂的类型约束。 Say, for example, you need to guarantee it's an IEnumerable because your Class is going to be iterating over it. 比如说,你需要保证它是一个IEnumerable因为你的Class将会迭代它。 In the case of these examples, you don't really need a type constraint at all. 在这些示例的情况下,您根本不需要类型约束。

If you find you're desperate to maintain a type constraint, though, you could try and leverage contra- and co-variance to constrain the child types, like so: 但是,如果您发现自己迫切希望保持类型约束,则可以尝试利用反对和协方差来约束子类型,如下所示:

class Foo<TProperty> : Class
    where TProperty : IProperty<object>
{
    // Same as your implementation
}

interface IProperty<out T>
{
    T Property;
}

class Property<T> : IProperty
{
    // Same as your implementation
}

EDIT: Added the IProperty interface because contra- and co-varience might only be definable on a class interface rather than it's implementation 编辑:添加了IProperty界面,因为反对和共同变量可能只能在类接口上定义而不是它的实现

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM