<t,tu,=“”等的目的=“”> </t,>

时间:2009-09-27 12:00:53

标签: c# generics

老实说,我不知道他们叫什么。 我一直无法在互联网上找到文章。

所以我明白你可以这样做:

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

但我很困惑,出于什么目的?某种性能提升? 据我所知,您可以使用这些东西(抱歉,这是什么名称?)来保存值。 但是你不会总是知道应该拥有什么样的数据吗? 例如:如果您需要存储产品,则只需制作产品类。当然,你知道它需要掌握什么样的数据,因为你正在设计代码。

所以是的,我想我的问题是:这个的目的是什么,与普通物体相比有什么优势;您指定了更多

我想我不清楚。我也想知道:有没有充分的理由去创造像上面那样的东西?例如,一个更具体的对象,例如将产品数据存储在产品对象中,而不是一些可以接收所有内容的通用对象。

新:更多文字: 另外,你怎么能处理完全通用的错误编码? 当我不知道我必须处理什么样的数据类型时,我担心会进行任何类型的数学操作或实际上任何操作。 如果它需要我为所有数据类型编写错误处理,那么我真的看不到这些通用参数的优点。

9 个答案:

答案 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类。我们可以ListOfApplesListOfBananasListOfInts等等。

我认为泛型提供了更好的解决方案。

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等表示的类型称为类型参数。表示实际类型,以便编译器可以从类型角度静态检查您的代码是否正确。

请参阅此introduction to generics

答案 5 :(得分:1)

您正在寻找的词是“泛型”。 它们允许您指定您将引用一个您不想指定的类型,并且将通过标签引用(例如:T)。 想象一下,你想写一个包含Int的类。 然后你还需要一个Double的版本,或者包含你自己的某个类的对象。 您只需要指定未指定的名称,然后在实例化对象时,您将指定T代表的内容,而不是编写此类内容的n个版本。

答案 6 :(得分:1)

它被称为泛型。 T和U是通用参数。我想T代表类型,而U只是字母表中的下一个字母。

它用于以下几个方面:提高性能,允许编译器捕获更多错误,并使代码更清晰。

特别是标准库具有通用容器,允许您指定它们包含的内容。

例如,

可用于从地图返回条目的键和值。

答案 7 :(得分:0)

这些是泛型类型参数,允许您定义泛型类的具体(实例)类型。

请查看this MSDN article以了解泛型。

答案 8 :(得分:0)

这是一种通用结构。您可以使用泛型来定义一个对所有类型的对进行操作的类,而不是为您需要的每种类型的对设计一个类。这允许您保持代码DRY - 而不是为每种可能类型的对重写相同的代码,您只需编写一次并重复使用它。