C#泛型入门指南

作为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 }; }

通过将两个整数值打包到一个对象实例中,可以返回任意数量的值。但是,如果还需要从另一个方法中返回一个整数和一个字符串呢?现在就需要创建一个像这样的类:

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"); }

这就是泛型之所以有用的原因。不是为每个特定场景创建一个类,而是创建一个泛型类,可以在许多场景中使用。

自己的Tuples

为了演示这里发生了什么,将创建自己的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。

现在看看第4行的构造函数。同样的类型参数被用来定义构造函数期望的类型,这就是为什么被允许将值1和2传递到构造函数中。两个public属性也使用类型参数来定义它们的类型,这允许将参数值设置为在构造函数中接收到的参数。

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

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

重要的是要理解twoInts的类型是MyTuple<int, int>。这可以通过这样做来证明:

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; } // 注意返回类型的类型参数已经被交换。 public MyTuple<TypeParameter2, TypeParameter1> SwapValues() { // 创建一个新的MyTuple实例,再次交换类型参数,以匹配方法本身的返回类型 return new MyTuple<TypeParameter2, TypeParameter1>(Value2, Value1); } }

现在添加了SwapValues()方法。它所做的就是返回一个新的MyTuple类的实例,两个值以及类型参数的位置都被交换了。重要的是要注意,正在使用创建tuple实例时发送的类型参数来定义SwapValues方法的返回类型。

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

这可能看起来不重要,但在继续之前,请确保理解为什么这个不会编译:

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

这个会编译:

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

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

例如,可以向MyTuple类添加这个方法:

public List<TypeParameter1> GetListOfValue1(int length) { // 创建要返回的列表 List<TypeParameter1> toReturn = new List<TypeParameter1>(); // 将Value1添加到列表中所需的次数 for (int i = 0; i < length; i++) toReturn.Add(Value1); return toReturn; }

这个新方法简单地创建了一个List,并用一定数量的Value1填充它,并返回列表。List<T>,就像自己的MyTuple<T1, T2>,是一个泛型类,所以发送类型参数。在GetListOfValue1()方法中,正在创建一个新的List<T>实例,将TypeParameter1作为List<T>所需的类型参数发送。

更复杂的例子

这是一个更复杂的例子:

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>

两个类型参数T1和T2被创建,并在方法的其余部分中使用。上面定义的类型参数用于定义CombineLists方法的返回类型,该方法返回一个包含MyTuple对象的列表,这些对象依次接收T1和T2作为它们的类型参数。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485