我知道泛型用于实现类型安全,我经常读到它们主要用于自定义集合。但为什么我们确实需要让它们通用?
例如,
为什么我不能使用string[]
代替List<string>
?
我们假设我声明了一个泛型类,它有一个泛型类型参数X。
T x;
如果我为类提供了一个
的方法x = x + 1;
实际上是什么意思?我不知道T
实际上会是什么,我不知道x = x + 1
实际上会发生什么。
如果我无法在我的方法中进行自己的操作,那么泛型如何帮助我呢?
我已经研究过很多书呆子的答案。如果任何人能够在此提供一些明确的见解,将不胜感激。
此致 NLV
答案 0 :(得分:17)
你的问题回答了自己。 数组类型本身就是一种通用类型。如果我们在CLR v1.0中放置泛型类型系统,我愿意打赌没有特殊的数组类型,只会有Array<T>
以及List<T>
等等。由于CLR的第一个版本没有泛型类型,因此我们将最重要和最明显的泛型类型(数组类型)放入其中。因此,在那里只有一大堆专用代码来处理数组,这是v1.0中支持的CLR的一种通用类型。
由于数组本质上是泛型类型,因此您的问题会自行解决:通用类型的原因通常与激发数组类型模式创建的原因相同。即:数组类型以特定方式放大其基础类型的能力。
int表示数字。 int []表示数字的集合;我们已经将数字概念放大数字集合的概念。客户代表客户。 Customer []代表了一组客户的概念。我们将客户的概念扩展到了客户收集的概念。 从类型T到类型T []的映射表示将类型实例一般放大到该类型实例集合的抽象概念。
同样的理由激励所有泛型类型。 Nullable<T>
类型将类型放大为“此事可能是该类型的实例”的概念。 IComparable<T>
类型将类型放大为“此类型的实例可以相对于另一个实例进行排序”的概念。等等。每个泛型类型都与数组模式类似:它表示将类型放大为新类型,为该类型提供新操作。
简而言之:通用类型系统的目的是使您能够发明自己的类型扩增,并使用类型系统来操纵这些扩增。
答案 1 :(得分:9)
当我们需要“高阶”多态时,我们使用泛型,类似于编译时duck typing。当我们说Foo<T>
时,我们的意思是Foo<>
取决于任何可能的T
可能具有的某些属性,并且给定的T
具有此属性,或者代码无法编译
当我们说x = x + 1
时,我们暗示T
能够添加1
,并且返回类型为T
。
答案 2 :(得分:3)
但为什么我们确实需要让它具有通用性?
如果您愿意,还可以制作大量自定义课程:StringList
,IntList
,DoubleList
等。但泛型的一点是您可以定义一次课程它适用于所有对象。
其中x = x + 1
属于通用类型x
的{{1}}要求实际的真实类型要么自然地支持添加,要么拥有{ {1}}重载了int作为第二种类型。
答案 3 :(得分:3)
实际上,我正在一个网站上工作,仿制药让我的工作变得更轻松。我需要从数据库访问大量信息,因为整个站点都是数据库驱动的,所以我真的想要一个单独的类来实现数据库功能。这个类由更多知道如何处理各自数据的类继承。
例如,我有一个处理产品数据的Products类和一个处理主题等的Themes类。好吧,所有这些类都需要一个通用的读写格式。所以,我创建了一个处理这个的记录类。
现在,这里是泛型乐趣开始的地方。我创建了一个Product类,Theme类等。使用强类型成员(如Name,Manufacturer,ThemeId),但Records类不知道如何处理它们。因此,Database类的实例化类型与特定类,Product,Theme等相关。并且Record类也使用Generic类型,因此我可以编写诸如......之类的代码。
Product.Name = "Cool Product";
然后将其存储为......
Products.InsertRecord(Product coolProduct);
它不仅节省了大量的输入,而且使我能够拥有一个处理所有脏工作的类,而这些小的“存根”类为我提供了一个可读的强类型接口。
无论如何,对不起啰嗦的帖子感到抱歉。希望这有助于您了解至少在这种情况下泛型的强大功能。
答案 4 :(得分:0)
对于您的第一个问题,如果您想在列表中动态添加字符串,则可以选择List<string>
而不是string[]
- 数组是固定大小。
答案 5 :(得分:0)
因为虽然支持string [](即扣除了自定义数组类型),但数组具有: *功能不如列表甚至是IList *是一个数组,因此这对其他类型的集合不起作用。字典怎么样?
如果我不能做自己的事 在我的方法中操纵如何 仿制药会帮助我 呢?
这有点像问“如果我经营素食餐厅,我可以在供应商那里购买牛排有什么用呢?”。
基本上,当您的项目变得更复杂时,您将在方法中编写自己的操作。你会做所有有意义的事情。
答案 6 :(得分:0)
public foobar<T>{
public Action<string,T> SomeAction;
public string SomeString;
public foobar(Action<string,T> s,string m){
SomeAction=s;
SomeString=m;
}
public void Run(T foo){
SomeAction(SomeString,foo);
}
}
如果没有泛型,您将不得不为每种可能性编写不同的类,或者其他一些精心设计的操作来获得所需的结果。泛型使事情变得更加优雅。
答案 7 :(得分:0)
string[]
为我们提供了一个固定大小的数组。要获得动态大小列表,我们需要一个类。因此,如果我们想要一个字符串列表,我们可能必须创建一个名为StringList的类。对于双打,DoubleList。等等。但是这些类是什么?这些类型是独一无二的?没有。因此,我们创建了一个可以接受任何类型的泛型类List&lt;&gt;。无需一遍又一遍地为不同类型重新创建相同的类,泛型可以为我们节省一些工作。
对于x = x + 1
问题,您必须期望T有一个+
运算符,它接受右侧的int。如果没有,则会抛出运行时错误。如果是,则该代码将x分配给+
运算符的输出,其中x
和1
作为参数。
还有很多其他情况没有执行任何独特的操作,并且类/方法必须使用多种类型。这些也是泛型是重要且有用的情况。
由您自己决定在代码中何时何地使用它们。如果你遇到问题,他们是最好的答案,那很好。使用它们。否则,不要。只是不要取消他们作为解决方案的资格,因为他们从来都不是你特定问题的答案。
答案 8 :(得分:0)
我自己的工作经验中的一个很好的例子是管理Linq-To-SQL类型。创建新数据库对象时我必须执行的一项基本操作是:
在这个特定的项目中,大约有100种不同的对象类型,因此为每个对象编写这些代码最好是麻烦的,即使是使用脚本,如果我们必须改变一些方法,我们该怎么做? / p>
使用泛型和约束,我可以这样做:
public class SQLObjectManager<TEntity> where TEntity : class, new()
{
public TEntity GetObject(int year, int stateID)
{
if( /* entity found in cache */)
return GetFromCache(year,stateID);
if( /* entity found in db */)
return GetFromDB(year,stateID);
TEntity entity = new TEntity();
entity.Year = year;
entity.StateID = stateID;
return entity;
}
}
除了编译时类型检查的好处之外,我现在只有一个函数来实现和维护我的数据库中每个表的新版本。
答案 9 :(得分:0)
请注意,如果您来自C ++背景,那么C#泛型和C ++模板是类似的概念。虽然,我相信他们的工作方式不同。
存在泛型,用于编写可跨不同类型重用的代码。仿制药,增加了类型安全性,减少了铸造和装箱。
假设您需要一堆整数;一个解决方案是提出一个整数堆栈类。然后,对于每个必需的数据类型(float,string等),您将编写该类的单独版本;在这里你会意识到这会导致代码重复。
另一种解决方案是编写一个使用object作为元素类型进行泛化的堆栈:
如何关注?
MyGenericStack<string> stringStack = new MyGenericStack<string>();
stringStack.Push("One");
stringStack.Push("Two");
stringStack.Push("Three");
MyGenericStack<int> intStack = new MyGenericStack<int>();
intStack.Push(1);
intStack.Push(2);
intStack.Push(3);
MyGenericStack<CustomObject> customObjectStack = new MyGenericStack<CustomObject>();
customObjectStack.Push(new CustomObject(1));
customObjectStack.Push(new CustomObject(2));
customObjectStack.Push(new CustomObject(3));
以下是通用类
public class MyGenericStack<T>
{
int position;
T[] data = new T[10];
public void Push(T obj) { data[position++] = obj; }
public T Pop() { return data[--position]; }
public void Show()
{
for (int i = 0; i < data.Length; i++)
{
//Console.WriteLine("Item: {0}", data[i]);
//MessageBox.Show(String.Format("Item: {0}", data[i]));
}
}
}
是的,我知道,现在你too
爱上了Generics!
顺便说一句,如果您计划比较继承和泛型,那么请记住,继承具有基本类型的可重用性,而泛型使用模板类型表示可重用性。