public class Account { 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; } }
The AccumulateSimple method is invoked like this:
decimal amount = Algorithms.AccumulateSimple(accounts);
The second version of the Accumulate method accepts any type that implements the interface 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; }
The new Accumulate method can be invoked by defining the Account type as a generic type parameter
decimal amount = Algorithm.Accumulate<Account>(accounts);
Because the generic type parameter can be automatically inferred by the compiler from the parameter type
of the method, it is valid to invoke the Accumulate method this way:
decimal amount = Algorithm.Accumulate(accounts);
public static T2 Accumulate<T1, T2>(IEnumerable<T1> source, Func<T1, T2, T2> action) { T2 sum = default(T2); foreach (T1 item in source) { sum = action(item, sum); } return sum; }
In calling this method, it is necessary to specify the generic parameter types because the compiler cannot infer this automatically.
decimal amount = Algorithm.Accumulate<Account, decimal>( accounts, (item, sum) => sum += item.Balance);
public class MethodOverloads { public void Foo<T>(T obj) =>Console.WriteLine($"Foo<T>(T obj), obj type: {obj.GetType().Name}"); public void Foo(int x) =>Console.WriteLine("Foo(int x)"); public void Foo<T1, T2>(T1 obj1, T2 obj2) => Console.WriteLine($"Foo<T1, T2>(T1 obj1, T2 obj2); " + $"{obj1.GetType().Name} {obj2.GetType().Name}"); 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); }
If an int is passed, then the method with the int parameter is selected. With any other parameter type, the compiler chooses the generic version of the method.
static void Main() { var test = new MethodOverloads(); test.Foo(33); test.Foo("abc"); test.Foo("abc", 42); test.Foo(33, "abc"); }
Running the program, you can see by the output that the method with the best match is taken:
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