2011-12-22 101 views
3

在C++中,你可以从一个模板参数调用方法的,像这样:是否可以在C#中调用泛型参数的方法?

template<class T> class foo 
{ 
    T t; 
    t.foo(); 
} 

但在C#中,看起来这是不可能的:

class foo<T> 
{ 
    T t; 
    public void foo() { 
    t.foo(); // Generates a compiler error 
    } 
}; 

我想这大概是不可能的在C#中,是吗?

回答

9

是的,如果你知道泛型类型的占位符T实现了从基类或接口的成员,您可以使用where条款约束型T该基类或接口。

public interface IFooable 
{ 
    void Foo(); 
} 

// ... 

public class Foo<T> where T : IFooable 
{ 
    private T _t; 

    // ... 

    public void DoFoo() 
    { 
     _t.Foo(); // works because we constrain T to IFooable. 
    } 
} 

这使得一般类型占位符T到被视为IFooable。如果你做而不是限制泛型中的泛型类型占位符,那么它被限制为object这意味着只有object的成员对泛型可见(即,你只能看到对object引用可见的成员,但调用任何被覆盖的成员将调用适当的覆盖)。

注:这是另外很重要的,因为事情像运算符重载(记住,符重载,未覆盖),所以如果你有这样的代码:

public bool SomeSuperEqualsChecker<T>(T one, T two) 
{ 
    return one == two; 
} 

这将始终使用object==即使Tstring。然而,如果我们有:

public bool SomeSuperEqualsChecker<T>(T one, T two) 
{ 
    // assume proper null checking exists... 
    return one.Equals(two); 
} 

这与string因为Equals()被覆盖,不超载正常工作。

所以,长和短只是记住一个无约束的通用占位符确实代表任何类型,但唯一可见的调用和操作是那些在object上可见的调用和操作。

除了接口/基类的限制,也有一些其他方面的限制:

  • new() - 意思是说,泛型类型的占位符必须有一个默认的构造函数
  • class - 意思是说,泛型类型的占位符必须是引用类型
  • struct - 意思是一般类型的占位符必须是一个值类型(枚举,原始的,结构等)

例如:

public class Foo<T> where T : new() 
{ 
    private T _t = new T(); // can only construct T if have new() constraint 
} 

public class ValueFoo<T> where T : struct 
{ 
    private T? _t; // to use nullable, T must be value type, constrains with struct 
} 

public class RefFoo<T> where T : class 
{ 
    private T _t = null; // can only assign type T to null if ref (or nullable val) 
} 

希望这会有所帮助。

+0

+1很好的答案! – Yuck 2011-12-23 22:23:49

3

您需要为您的方法添加type constraint

public interface IFoo { 
    void Foo(); 
} 

public class Foo<T> where T : IFoo { 
    T t; 
    public void foo() { 
     t.Foo(); // Generates a compiler error 
    } 
} 
3

如果您愿意接受泛型类型的约束,这意味着您的泛型类型必须受限于从某个基类派生或实现某个接口。

例子:

abstract class SomeBase 
{ 
    public abstract DoSomething(); 
} 

// new() ensures that there is a default constructor to instantiate the class 
class Foo<T> where T : SomeBase, new() 
{ 
    T t; 

    public Foo() 
    { 
     this.t = new T(); 
     this.t.DoSomething(); // allowed because T must derive from SomeBase 
    } 
} 
11

您发现之间模板仿制药的差异。尽管它们看起来很相似,但实际上却完全不同。

一个模板只需要为实际提供的类型参数是正确的;如果你提供一个没有foo方法的T,那么编译失败;如果仅提供具有foo的类型参数,则编译成功。

相比之下,任何可能的T都必须正确对应于。由于我们没有证据表明每个可能的T都会有一个foo的方法,所以通用是非法的。

相关问题