c# 高级编程 5章113页 【泛型】【泛型方法】

139 阅读2分钟

泛型方法

  • 允许在非泛型类中定义泛型方法
  • 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