泛型方法
- 允许在非泛型类中定义泛型方法
- T可以是参数或局部变量
- 两种调用方式:都有效,因为编译器会自行推断类型
// 泛型方法
void Swap<T>(ref T x, ref T y)
{
T temp;
temp = x;
x = y;
y = temp;
}
int i = 4;
int j = 5;
//调用方式 1:
Swap<int>(ref i, ref j);
//调用方式 2:
Swap(ref i, ref j);
带约束的泛型方法
- where子句用来限制
T
,同样适用于泛型方法
public interface IAccount
{
decimal Balance { get; }
string Name { get; }
}
// 让 Account 实现接口 IAccount
public class Account : IAccount
{
public string Name { get; }
public decimal Balance { get; }
public Account(string name, Decimal balance)
{
Name = name;
Balance = balance;
}
}
public static class Algorithms
{
// 如果不用泛型方法这个技术,长这样:
public static decimal AccumulateSimple(IEnumerable<Account> source)
{
decimal sum = 0;
foreach (Account a in source)
{
sum += a.Balance;
}
return sum;
}
// 使用了泛型方法这个技术,并且加了接口约束,长这样:
// 好处是:这个方法能够接收,实现了IAccount类的任意类型
public static decimal Accumulate<TAccount>(IEnumerable<TAccount> source)
where TAccount : IAccount
{
decimal sum = 0;
foreach (TAccount a in source)
{
sum += a.Balance;
}
return sum;
}
}
class Program
{
static void Main()
{
var accounts = new List<Account>()
{
new Account("Christian", 1500),
new Account("Stephanie", 2200),
new Account("Angela", 1800),
new Account("Matthias", 2400),
new Account("Katharina", 3800)
};
decimal amount = Algorithms.AccumulateSimple(accounts);
Console.WriteLine($"result of {nameof(Algorithms.AccumulateSimple)}: {amount}");
amount = Algorithms.Accumulate(accounts);
Console.WriteLine($"result of {nameof(Algorithms.Accumulate)}: {amount}");
}
}
输出:
result of AccumulateSimple: 11700
result of Accumulate: 11700
这里T实现IAccount接口的要求,其实过于严格
带委托的泛型方法
- 泛型方法的参数,允许是一个泛型委托
- 编译器不能推断类型,因此,需要明确指定T的实际类型。
//方法定义
public static T2 Accumulate<T1, T2>(IEnumerable<T1> source, Func<T1, T2, T2> action)
{
// TODO: update to C# 7.1 syntax
T2 sum = default(T2);
foreach (T1 item in source)
{
sum = action(item, sum);
}
return sum;
}
//方法调用
//<Account, decimal>不可缺省,因为编译器无法对lamda表达式表示的方法来推断类型
amount = Algorithms.Accumulate<Account, decimal>(accounts, (item, sum) => sum += item.Balance);
泛型方法的重载
- 编译期间,会使用最佳匹配
public class MethodOverloads
{
//版本1
public void Foo<T>(T obj) =>
Console.WriteLine($"Foo<T>(T obj), obj type: {obj.GetType().Name}");
//版本2,是版本1的int专用版
public void Foo(int x) =>
Console.WriteLine("Foo(int x)");
//版本3
public void Foo<T1, T2>(T1 obj1, T2 obj2) =>
Console.WriteLine($"Foo<T1, T2>(T1 obj1, T2 obj2); {obj1.GetType().Name} {obj2.GetType().Name}");
//版本4,是版本3的int专用版
public void Foo<T>(int obj1, T obj2) =>
Console.WriteLine($"Foo<T>(int obj1, T obj2); {obj2.GetType().Name}");
public void Bar<T>(T obj) =>
Foo(obj);
}
class Program
{
static void Main()
{
var test = new MethodOverloads();
//匹配到版本2,即版本1的int专用版
test.Foo(33);
//匹配到版本1
test.Foo("abc");
//匹配到版本3
test.Foo("abc", 42);
//匹配到版本4,即版本3的int专用版
test.Foo(33, "abc");
//Bar()调用了Foo()
//但是匹配到的是:版本1,而不是版本1的int专用版
//Bar()具体调用哪个Foo(),是在编译期间决定的,而不是运行期间决定的
test.Bar(44);
}
}
输出:
//最佳匹配
Foo(int x)
Foo<T>(T obj), obj type: String
Foo<T1, T2>(T1 obj1, T2 obj2); String Int32
Foo<T>(int obj1, T obj2); String
Foo<T>(T obj), obj type: Int32