C# 泛型入门 - 第一部分






4.86/5 (44投票s)
一篇关于 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};
}
通过简单地将两个整数打包成一个对象实例,我们可以返回任意多个值。但是,如果我们还需要从另一个方法返回一个整数和一个 `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
这是一个更复杂的例子:
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
上面定义的类型参数用于定义 `CombineLists` 方法的返回类型,该方法返回一个 `MyTuple` 对象列表,而 `MyTuple` 对象又接收 `T1` 和 `T2` 作为其类型参数。
`CombineLists` 方法接受两个参数。这是两个列表。第一个列表使用类型参数 `T1` 作为其类型,第二个列表使用 `T2`。
结论
这应该是对泛型基础知识的一个很好的介绍。仍有许多内容有待涵盖,我计划在未来的文章中进行。