将类型约束到接口的目的是什么?

JSB*_*ոգչ 6 c# generics interface constraints

允许以下内容的目的是什么?

class A<T> where T : IFoo {
    private T t;
    A(T t) { this.t = t; }
    /* etc */
}
Run Code Online (Sandbox Code Playgroud)

这与仅仅声明A需要一个IFoo需要它的地方有什么不同?

class A {
    private IFoo foo;
    A(IFoo foo) { this.foo = foo; }
    /* etc */
}
Run Code Online (Sandbox Code Playgroud)

我能看到的唯一区别是,在第一种情况下,我保证认为A<T>将始终与一个实例化T实现IFoo 所有对象的A将是相同的基本类型.但对于我的生活,我无法弄清楚为什么我需要这样的约束.

Dog*_*ett 7

两个示例的主要区别在于,class A无论何时将变量定义为T,您都可以使用该变量上的所有属性/函数,这些属性/函数也在IFoo中定义.

然而,在class BIFoo只是为泛型类型参数的名称,因此,只要你声明一个变量的类里面的IFoo,就好像它是一个你只能用它object的类型.

例如,如果

public interface IFoo
{
   int Value { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以做到这一点 class A

class A<T> where T : IFoo 
{
     public void DoSomething(T value)
     {
          value.Value++;
     }
}
Run Code Online (Sandbox Code Playgroud)

如果您尝试相同的操作,class B则会遇到编译器错误,该类型IFoo不包含属性Value或类似内容.原因是<IFoo>B类只是一个名称而且与界面无关,你可以把它称为任何你喜欢的东西.

更新:

class B {
    private IFoo foo;
    B(IFoo foo) { this.foo = foo; }
    /* etc */
}
Run Code Online (Sandbox Code Playgroud)

这个构造确实基本相同,除非IFoo再次暴露回外部,请考虑两个类中的以下属性

A类:

public T Foo { get { return foo; }}
Run Code Online (Sandbox Code Playgroud)

B级:

public IFoo Foo { get { return foo; }}
Run Code Online (Sandbox Code Playgroud)

现在考虑用C类初始化两个类,定义为C类

public class FooClass : IFoo
{
    public int Value { get; set; }
    public int SomeOtherValue { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后考虑定义为2个变量

var foo = new FooClass();
var a = new A<FooClass>(foo);
var b = new B(foo);
Run Code Online (Sandbox Code Playgroud)

现在设置你可以做的SomeOtherValue使用a

a.Foo.SomeOtherValue = 2;
Run Code Online (Sandbox Code Playgroud)

而你必须这样做

((FooClass)b.Foo).SomeOtherValue = 2;
Run Code Online (Sandbox Code Playgroud)

希望有意义;-)


Dan*_*Tao 2

编辑:起初我以为你很愚蠢:第一个例子肯定无法编译。只有在自己尝试过(并且看到它确实可以编译)之后,我才意识到Doggett 已经指出的内容:您的class B<IFoo>示例实际上与接口没有关系IFoo;它只是一个泛型类型,其类型参数恰好被调用IFoo

\n\n

也许您已经意识到了这一点,并且您确实在问:“为什么我要限制泛型类型参数?” 如果是这样的话,那么我认为其他答案已经在某种程度上解决了这个问题。但听起来你好像在问,“为什么我要这样定义我的类型而不是这样(因为它们实际上是相同的)?” 答案很简单:它们一样。

\n\n
\n\n

现在,这是另一个问题\xe2\x80\x94,你没有问,但我最初打算回答这个问题;)

\n\n

为什么要定义这样的类型:

\n\n
class A<T> where T : IFoo\n{\n    T GetFoo();\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

...而不是这个?

\n\n
class A\n{\n    IFoo GetFoo();\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

想到的一个原因是(因为它类似于我过去处理过的场景):您设计的不是一个类,而是一个小的类层次结构,并且IFoo只是“基线”接口您的所有类都需要,而有些类可能会利用特定的实现或更多派生的接口。

\n\n

这是一个愚蠢的例子:

\n\n
class SortedListBase<T, TList> where TList : IList<T>, new()\n{\n    protected TList _list = new TList();\n\n    // Here\'s a method I can provide using any IList<T> implementation.\n    public T this[int index]\n    {\n        get { return _list[index]; }\n    }\n\n    // Here\'s one way I can ensure the list is always sorted. Better ways\n    // might be available for certain IList<T> implementations...\n    public virtual void Add(T item)\n    {\n        IComparer<T> comparer = Comparer<T>.Default;\n        for (int i = 0; i < _list.Count; ++i)\n        {\n            if (comparer.Compare(item, _list[i]) < 0)\n            {\n                _list.Insert(i, item);\n                return;\n            }\n        }\n\n        _list.Add(item);\n    }\n}\n\nclass SortedList<T> : SortedListBase<T, List<T>>\n{\n    // Here is a smarter implementation, dependent on List<T>\'s\n    // BinarySearch method. Note that this implementation would not\n    // be possible (or anyway, would be less direct) if SortedListBase\'s\n    // _list member were simply defined as IList<T>.\n    public override void Add(T item)\n    {\n        int insertionIndex = _list.BinarySearch(item);\n\n        if (insertionIndex < 0)\n        {\n            insertionIndex = ~insertionIndex;\n        }\n\n        _list.Insert(insertionIndex, item);\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n