我在MSDN上阅读了一篇关于C#泛型的优秀文章。
我脑海中浮现出的问题是:为什么我应该使用泛型约束?
例如,如果我使用以下代码:
public class MyClass<T> where T : ISomething
{
}
我能否在该类中将所有T
的引用都替换为ISomething
?
采用这种方法有什么好处?
我在MSDN上阅读了一篇关于C#泛型的优秀文章。
我脑海中浮现出的问题是:为什么我应该使用泛型约束?
例如,如果我使用以下代码:
public class MyClass<T> where T : ISomething
{
}
我能否在该类中将所有T
的引用都替换为ISomething
?
采用这种方法有什么好处?
ISomething
替换该类中所有的T
吗?” 我认为你的意思是比较以下内容:public class MyClass<T> where T : ISomething
{
public T MyProperty { get; set; }
}
使用:
public class MyClass
{
public ISomething MyProperty { get; set; }
}
MyProperty
只能保证是ISomething
的一个实例。而在第一个例子中,MyProperty
是T
的任何类型,即使它是ISomething
的具体子类型。考虑一个ISomething
的具体实现:public class MySomething : ISomething
{
public string MyOtherProperty { get; set; }
}
MyClass<MySomething> myClass = new MyClass<MySomething>();
Console.WriteLine(myClass.MyProperty.MyOtherProperty);
另一方面,如果我们使用第二个示例,由于MyOtherProperty
仅被认为是一个ISomething
,因此我们无法访问它:
MyClass myClass = new MyClass();
Console.WriteLine(myClass.MyProperty.MyOtherProperty); // Won't compile, no property "MyOtherProperty"
另外,这些类型约束的有用之处在于您可以引用 MyProperty
(类型为 T
)并访问 ISomething
的成员。换句话说,如果声明了 ISomething
如下:
public interface ISomething
{
public string SomeProperty { get; set; }
}
那么你可以访问 MyProperty.SomeProperty
。 如果省略了where T : ISomething
,则无法访问SomeProperty
,因为T
只能被认为是object
类型。
类型安全。例如,假设你正在创建一个容器。你可以通过将该容器参数化来传递一些内容,并以正确的形式检索它,而不必进行任何强制转换。你只是在定义存储在容器中的事物类型的约束。
List<>
的区别如下:IListElement
。现在想象一下,您有一个类似于这样的对象。class Element : IListElement
{
public string Something { get; set; }
}
现在我只需要执行 list.Add(element);
就可以了,这样与真正的 List<Element>
没有区别。但是当我检索数据时情况就不同了,如果我使用使用 IListElement
的列表,那么我必须将我的数据强制转换回来,以便我可以从中获取 Something
。因此,我需要执行以下操作:
string s = ((Element)list[0]).Something;
对于通用的情况,我只需要执行以下操作:
string s = list[0].Something;
它可以避免很多麻烦,当然它的功能不止于此,但我认为你可以从这里得到一些想法。
首先,你可以在通用方法/通用类的代码中调用ISomething中定义的方法。如果T允许是任何类型,则不可能实现此操作(尽管您始终可以进行一些运行时转换)。
这使您能够对T的可选类型施加编译时约束,并因此在编写代码时依靠这些约束-将运行时错误转换为编译时错误。
你的类的消费者将受益于增加的类型安全性等方面。
class Widget : IPokable { }
// No generics
Widget w = (Widget)list[0]; // cast can fail
// With generics
Widget w = list[0];
如果没有泛型,如果列表包含IPokable
对象,则仍然需要强制转换。
您正在实现的类受益于在泛型对象上使用特定方法。
class PokableList<T> where T : IPokable {
public T PokeAndGet() {
currentObj.Poke();
return currentObj;
}
}
是的,您可以使用ISomething代替T,但这将手动将泛型类型关闭为普通类。它将不再是泛型类型。通过使用T,您可以将类型保持开放到尽可能多的ISomething子类型。在这里,代码重用而不会影响类型安全性是关键优势。例如,如果您使用一个ISomethings的堆栈,您可以将任何ISomething推入堆栈,但必须使用向下转换到ISomething的实际子类型才能进行弹出以使其有用。向下转换创建了潜在的故障点,在泛型Stack<T>中不存在,其中T:ISomething。
public class ListOfCars<T> : List<T> where T : Car { }
public abstract class Car { }
public class Porsche : Car { }
public class Bmw : Car { }
...然后如果我写下这段代码:
var porsches = new ListOfCars<Porsche>();
// OK
porsches.Add(new Porsche());
//Error - Can't add BMW's to Porsche List
porsches.Add(new Bmw());