65.9K
CodeProject 正在变化。 阅读更多。
Home

C# 泛型入门 - 第一部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.86/5 (44投票s)

2015年11月16日

CPOL

4分钟阅读

viewsIcon

63240

一篇关于 C# 泛型的教程,面向初学者

文章索引

C# 泛型入门 - 第一部分

C# 泛型入门 - 第一部分2

引言

当我还是 C# 新手时,我记得我对泛型感到畏惧。如果我能时光旅行,这就是我希望寄回给过去的自己的一篇文章。换句话说,这篇文章是面向初学者的,希望它能让许多刚开始学习 C# 的人不再对泛型感到困惑。

我将从非常简单的内容开始,然后慢慢增加复杂性。

所以,我们不要浪费时间闲聊了,直接开始吧。

入门

这是一个非常简单的方法。它返回一个整数。

public int ReturnsOneInt()
{
    return 1;
}

但是,如果我们想从这个方法中返回多个整数呢?方法一次只能返回一个东西,所以如果我们想返回两个整数,我们需要像这样修改它:

public class TwoInts
{
    public int Int1 { get; set; }
    public int Int2 { get; set; }
}
public TwoInts ReturnsTwoInts()
{
    return new TwoInts() {Int1 = 1, Int2 = 2};
}

通过简单地将两个整数打包成一个对象实例,我们可以返回任意多个值。但是,如果我们还需要从另一个方法返回一个整数和一个 `string` 呢?我们现在必须创建一个像这样的类:

public class IntAndString
{
    public int Int1 { get; set; }
    public string String1 { get; set; }
}

很快,你的代码就会充斥着这些小类。有一个更好的方法。.NET 有一个内置的泛型类叫做 `Tuple`。

你会像这样使用它:

public Tuple<int,int> ReturnsTwoInts()
{
    return new Tuple<int, int>(1,2);
}

或者

public Tuple<int,string> ReturnsIntAndString()
{
    return new Tuple<int, string>(1,"two");
}

这就是泛型的用处。我们不是为每种特定场景创建一个类,而是创建一个泛型类,它可以用于许多场景。

我们自己的 Tuple

为了演示这里发生的事情,我们将创建我们自己的 `tuple` 类。

代码如下:

public class MyTuple<TypeParameter1, TypeParameter2> 
{
    public TypeParameter1 Value1 { get; set; } 
    public TypeParameter2 Value2 { get; set; } 

    public MyTuple(TypeParameter1 value1, TypeParameter2 value2)
    {
        Value2 = value2; 
        Value1 = value1; 
    }
}

在第一行,你会看到类声明。这里唯一新的是两个类型参数。类型参数是你必须传递给泛型类的参数,就像你传递给方法的参数一样。但是,我们不是在圆括号 `()` 中传递参数,而是在尖括号 `<>` 中传递。

你像这样将类型参数传递给泛型类:

MyTuple<int,int> twoInts = new MyTuple<int,int>(1,2);

在这里,我们传递了两个 `int` 类型作为类型参数,所以 `TypeParameter1` 和 `TypeParameter2` 都将是 `int`。

现在看看第四行上的构造函数。相同的类型参数用于定义构造函数期望的类型,这就是为什么我们允许将值 1 和 2 发送到构造函数中的原因。这两个 `public` 属性也使用类型参数来定义它们的类型,这允许我们将参数值设置为我们在构造函数中接收的参数。

这也是为什么我们现在可以这样做:

int integerValue = twoInts.Value1;
integerValue = twoInts.Value2;

理解 `twoInts` 的类型是 `MyTuple` 非常重要。这可以通过这样做来证明:

MyTuple<int, int> anotherTwoInts = twoInts; //COMPILES!

下一个示例将无法编译,因为当它们的类型不同时(请注意第二个类型变量是 `string`),不允许将一个变量设置为另一个变量。

MyTuple<int, string> anotherTwoInts = twoInts; //DOES NOT COMPILE!

交换值

让我们为我们的 `tuple` 类添加一些功能。

public class MyTuple<TypeParameter1, TypeParameter2>
{
    public TypeParameter1 Value1 { get; set; }
    public TypeParameter2 Value2 { get; set; }

    public MyTuple(TypeParameter1 value1, TypeParameter2 value2)
    {
        Value2 = value2;
        Value1 = value1;
    }
    //notice that the type parameters of the return type has been swapped.
    public MyTuple<TypeParameter2, TypeParameter1> SwapValues()
    {
        //create a new instance if MyTuple, again with type parameters swapped to that it matches
        //the return type of the method itself
        return new MyTuple<TypeParameter2, TypeParameter1>(Value2, Value1);
    }
}

我们现在已经添加了 `SwapValues()` 方法。

它所做的就是返回一个 `MyTuple` 类的新实例,其中两个值以及类型参数的位置被交换了。这里需要注意的重要一点是,我们正在使用在创建元组实例时传递的类型参数来定义 `SwapValues` 方法的返回类型。

再次强调,理解这些泛型类实例的类型如何发挥作用非常重要。

这可能看起来不重要,但在继续之前,请确保你理解为什么这不起作用

MyTuple<int, string> swapped = intAndString.SwapValues(); //does NOT compile

而这个可以编译

MyTuple<int, string> swappedAgain = intAndString.SwapValues().SwapValues();

另一个有趣的事情是,我们能够在一个泛型类中创建另一个泛型类的实例。这是可行的,因为我们可以将接收到的类型参数传递给其他泛型类。

例如,我们可以将此方法添加到 `MyTuple` 类中:

public List<TypeParameter1> GetListOfValue1(int length)
{
    //create the list to return
    List<TypeParameter1> toReturn = new List<TypeParameter1>();
    //add Value1 to the list the required amount of times
    for (int i = 0; i < length; i++) toReturn.Add(Value1);
    return toReturn;
}

这个新方法只是创建一个 `List`,并用一定数量的 `Value1` 填充它,然后返回 `list`。

`List`,就像我们自己的 `MyTuple` 一样,是一个泛型类,所以我们传递类型参数。在 `GetListOfValue1()` 方法中,我们正在创建一个 `List` 的新实例,并将 `TypeParameter1` 作为 `List` 所需的类型参数传递。

这是一个更复杂的例子:

public static List<MyTuple<T1, T2>> CombineLists<T1, T2>(List<T1> lst1, List<T2> lst2)
{
    List<MyTuple<T1, T2>> toReturn = new List<MyTuple<T1, T2>>();
    for (int i = 0; i < lst1.Count(); i++)
    {
        toReturn.Add(new MyTuple<T1, T2>(lst1[i], lst2[i]));
    }
    return toReturn;
}

此方法接受两个列表作为参数,然后将列表中的项合并成一个元组列表。请注意,列表必须长度相等,否则会出错。

类型参数定义在此处:`CombineLists`。创建了两个类型参数 `T1` 和 `T2`,并在方法中使用它们。

上面定义的类型参数用于定义 `CombineLists` 方法的返回类型,该方法返回一个 `MyTuple` 对象列表,而 `MyTuple` 对象又接收 `T1` 和 `T2` 作为其类型参数。

`CombineLists` 方法接受两个参数。这是两个列表。第一个列表使用类型参数 `T1` 作为其类型,第二个列表使用 `T2`。

结论

这应该是对泛型基础知识的一个很好的介绍。仍有许多内容有待涵盖,我计划在未来的文章中进行。

 

© . All rights reserved.