我在MSDN上阅读了一篇关于C#中泛型的优秀文章。
我脑子里浮现的问题是 - 我为什么要使用通用约束?
例如,如果我使用这样的代码:
public class MyClass<T> where T : ISomething
{
}
我无法使用T
切换此类中ISomething
的所有引用?
使用这种方法有什么好处?
答案 0 :(得分:47)
你问,“我不能用T
切换此课程中ISomething
的所有引用吗?”所以我认为你的意思是比较:
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
类型。
答案 1 :(得分:8)
类型安全。例如,假设您正在创建一个容器。您可以将某些内容传递给该容器并以适当的形式检索它,而不必在以后通过参数化容器进行任何转换。您只是定义了您愿意存储在容器中的事物类型的约束。
答案 2 :(得分:4)
以下是仅使用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;
节省了很多麻烦,当然它比这更进一步,但我认为你可以从中得到这个想法。
答案 3 :(得分:2)
首先,您可以在泛型类的泛型方法/方法的代码中调用ISomething中定义的方法。如果允许T为任何类型,那么这是不可能的(尽管你总是可以做一些运行时转换)。
因此,它允许您对T可以强制执行编译时约束,因此在编写代码时依赖于这些约束 - 将运行时错误转换为编译时错误。
答案 4 :(得分:1)
是的,您可以使用ISomething代替T,但这将手动 关闭 泛型类型到普通类。它不再是通用类型。通过使用T,您可以将 打开 类型保留为所需数量的ISomething子类型。 代码重用而不影响类型安全性是这里的关键优势。例如,如果您使用堆栈的ISomethings,您可以将任何ISomething推送到堆栈,但是必须在向实际子类型转发时出现pop ISomething让它变得有用。向下转换会产生一个潜在的失败点,这在通用Stack<T>
中不存在,其中T:ISomething
答案 5 :(得分:0)
您班级的消费者可以获得提高类型安全性的好处。
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;
}
}
答案 6 :(得分:0)
也许这个简单的例子可能会有所帮助。
如果我有这些课程:
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());
您可以看到我无法将 BMW 添加到 Porsche 列表中,但如果我只是从基类中编程,则可以。