老实说,我不知道他们叫什么。 我一直无法在互联网上找到文章。
所以我明白你可以这样做:
public struct Pair<T, U>
{
public readonly T Value1;
public readonly U Value2;
public Pair(T fst, U snd)
{
this.Value1 = fst;
this.Value2 = snd;
}
public override String ToString()
{
return "(" + Value1 + ", " + Value2 + ")";
}
public Pair<U, T> Swap()
{
return new Pair<U, T>(Value2, Value1);
}
}
也可以是class
而不是struct
。
但我很困惑,出于什么目的?某种性能提升? 据我所知,您可以使用这些东西(抱歉,这是什么名称?)来保存值。 但是你不会总是知道应该拥有什么样的数据吗? 例如:如果您需要存储产品,则只需制作产品类。当然,你知道它需要掌握什么样的数据,因为你正在设计代码。
所以是的,我想我的问题是:这个的目的是什么,与普通物体相比有什么优势;您指定了更多
我想我不清楚。我也想知道:有没有充分的理由去创造像上面那样的东西?例如,一个更具体的对象,例如将产品数据存储在产品对象中,而不是一些可以接收所有内容的通用对象。
新:更多文字: 另外,你怎么能处理完全通用的错误编码? 当我不知道我必须处理什么样的数据类型时,我担心会进行任何类型的数学操作或实际上任何操作。 如果它需要我为所有数据类型编写错误处理,那么我真的看不到这些通用参数的优点。
答案 0 :(得分:8)
假设您有两种情况,代码是相同的,除了代码中引用的类型之一是不同的。
能够将代码编写一次,然后在两种情况下使用相同的代码都不是很有用吗?例如,像List
这样的容器类 - 应该清楚的是,每次要在其中存储特定类型时,必须编写不同的List
类是很痛苦的。
它们被称为类型参数,需要参数的类型是泛型类型。
一个非常流行的数据结构是Dictionary<TKey, TValue>
。它将键映射到值,因此您可以查找给定键的值。例如,密钥可能是“姓氏”,值可能是“电话号码” - 在这种情况下,两者都是字符串类型(电话号码并不总是数字)。
循环浏览Dictionary
的内容时,它存储的每个项目都是一对。在标准类中,类型为KeyValuePair<TKey, TValue>
:
for (var pair in myDict)
{
Console.WriteLine(pair.Key + " maps to " + pair.Value);
}
答案 1 :(得分:6)
目的是通用性。通常,你有一个类,其行为并不真正取决于它所使用的类型。
而不是Pair
,请考虑.NET类List<T>
。它存储了某些类型的列表。该列表没有,不应该关心它存储的类型。它只需保证只该类型可以存储。
List<string>
允许我存储字符串。 List<int>
允许我存储整数。
如果我们没有泛型,我们必须抛弃类型安全,并使用一个只存储Object
的List类,但是没有什么可以防止我存储Apple
s。
Banana
或者我们可以为我们需要存储的每种类型编写一个新的List
类。我们可以ListOfApples
,ListOfBananas
,ListOfInts
等等。
我认为泛型提供了更好的解决方案。
Pair
示例可能不那么明显,因为often
,如果我们需要精确存储两个值,那是因为它们有一些明确的关系,应该通过创建一个特定的对象来表示。但有时,他们没有。有时它们只是“第一价值”和“第二价值”。也许你有一个只返回两个值的函数。返回Pair<T1, T2
比返回一个值并通过out
参数处理另一个值更容易。
你经常不需要一双,不。但 if 你真的需要“一种方法在单个对象中存储两个不同类型的值”,如果没有与之关联的真实行为,那么Pair
类就能很好地表示。然后,为什么不通过允许类型更改来使其可重用?
另外,如何处理完全通用的错误编码?当我不知道我必须处理什么样的数据类型时,我担心会进行任何类型的数学操作或实际上任何操作。如果它需要我为所有数据类型编写错误处理,那么我真的看不到这些通用参数的优点。
简单:.NET不允许你这样做。在你的Pair示例中,如果你尝试使用一个不保证可用的函数,编译器会抱怨。在您的情况下,您将只能使用Object
基类上定义的函数(当然,也可以使用任何类型的泛型函数和类)。
所以你不能对这些泛型类型使用任何类型的数学操作。除了存储它们并传递它们(并调用.ToString()
)之外,你不能做更多的事情。
但有时候,你不需要 。例如,如果您只是创建List
类或Pair
。
您还可以指定约束,将其缩小一点。例如:
void DoStuffWithStream(T arg) where T : Stream
{
// in this function that T is some type derived from Stream, so we can use all methods that would work on a Stream.
}
如果我尝试使用int
调用它,则会产生编译错误,但会与所有不同的Stream
子类一起使用。现在我对课程了解得更多,所以我将能够有意义地进行一些操作。
但是回到错误处理,你需要在List
类中处理什么错误?不会发生很多错误。
你有一个类,它的工作是存储一些未知(但固定)类型的可变数量的对象。它可以插入新对象(可能会抛出内存不足异常),并且可以检索对象。它可以让我们搜索对象,当然,当我们搜索到的东西找不到时,我们可能会遇到错误情况。但基本上就是这样。没有特定于泛型类型的错误处理。为什么会这样?我们不会对我们存储的对象执行任何类型特定的操作,因此它们实际上没有机会抛出任何特定于类型的错误。
答案 2 :(得分:4)
这些东西被称为泛型,它们的目的是帮助您推广算法,容器和方法,而不会产生在运行时转换值的性能开销。
例如,在Pair类中,考虑一下您的选择:
一个。你可以编写一个包含两个对象的类(作为.Net 2之前的所有容器),但是你必须打包或转换你的类型才能使用它,你就没有任何类型的安全性。
B中。你的另一个选择是为每种类型编写一个特定的Pair类,这可能很难,也不是那么明智。 (例如,查看StringCollection)。
现在,假设您可以编写一个可以采用任何类型而没有性能开销的类。不是很好吗?通常,通过编写Pair<U,T>
,您可以告诉编译器为每种用法生成特定的Pair类。编写Pair<string, int>
将使编译器为这些类型生成特定的Pair类。
注意: C#泛型的工作方式与C ++模板有所不同,我在这里描述的C#泛型的“工作原理”并不完全是最新的。我想,这很容易理解。您可以阅读有关该主题的更多信息here。
答案 3 :(得分:3)
您在这里看到的是一个称为“对”的容器的典型定义(检查stl对或std对)。
Pair是一个只能容纳2个值的容器。这些值可以是不同的类型。
例如:
Pair<int, float> p = new Pair<int float>()
将创建一个包含int和float的新容器:)
在你的情况下,T将成为一个int,U将成为一个浮点数。说实话,这是一个非常简单的模板定义。
至于你的评论是否用于在现实世界中保存数据 - 哎呀。查看地图容器 - 他们使用很多对:)
答案 4 :(得分:1)
这称为 Generics ,T,U等表示的类型称为类型参数。表示实际类型,以便编译器可以从类型角度静态检查您的代码是否正确。
答案 5 :(得分:1)
您正在寻找的词是“泛型”。 它们允许您指定您将引用一个您不想指定的类型,并且将通过标签引用(例如:T)。 想象一下,你想写一个包含Int的类。 然后你还需要一个Double的版本,或者包含你自己的某个类的对象。 您只需要指定未指定的名称,然后在实例化对象时,您将指定T代表的内容,而不是编写此类内容的n个版本。
答案 6 :(得分:1)
它被称为泛型。 T和U是通用参数。我想T代表类型,而U只是字母表中的下一个字母。
它用于以下几个方面:提高性能,允许编译器捕获更多错误,并使代码更清晰。
特别是标准库具有通用容器,允许您指定它们包含的内容。
例如,可用于从地图返回条目的键和值。
答案 7 :(得分:0)
这些是泛型类型参数,允许您定义泛型类的具体(实例)类型。
请查看this MSDN article以了解泛型。
答案 8 :(得分:0)
这是一种通用结构。您可以使用泛型来定义一个对所有类型的对进行操作的类,而不是为您需要的每种类型的对设计一个类。这允许您保持代码DRY - 而不是为每种可能类型的对重写相同的代码,您只需编写一次并重复使用它。