![]() CATEGORIES: BiologyChemistryConstructionCultureEcologyEconomyElectronicsFinanceGeographyHistoryInformaticsLawMathematicsMechanicsMedicineOtherPedagogyPhilosophyPhysicsPolicyPsychologySociologySportTourism |
Nbsp; Generic MethodsWhen you define a generic class, struct, or interface, any methods defined in these types can refer to a type parameter specified by the type. A type parameter can be used as a method’s parameter, a method’s return type, or as a local variable defined inside the method. However, the CLR also sup- ports the ability for a method to specify its very own type parameters. And these type parameters can also be used for parameters, return types, or local variables. Here is a somewhat contrived example of a type that defines a type parameter and a method that has its very own type parameter.
internal sealed class GenericType<T> { private T m_value;
public GenericType(T value) { m_value = value; }
public TOutput Converter<TOutput>() { TOutput result = (TOutput) Convert.ChangeType(m_value, typeof(TOutput)); return result; } }
In this example, you can see that the GenericType class defines its own type parameter (T), and the Converter method defines its own type parameter (TOutput). This allows a GenericType to be constructed to work with any type. The Converter method can convert the object referred to by the m_value field to various types depending on what type argument is passed to it when called. The ability to have type parameters and method parameters allows for phenomenal flexibility. A reasonably good example of a generic method is the Swap method.
private static void Swap<T>(ref T o1, ref T o2) { T temp = o1; o1 = o2; o2 = temp; }
Code can now call Swap like as follows.
private static void CallingSwap() { Int32 n1 = 1, n2 = 2; Console.WriteLine("n1={0}, n2={1}", n1, n2); Swap<Int32>(ref n1, ref n2); Console.WriteLine("n1={0}, n2={1}", n1, n2);
String s1 = "Aidan", s2 = "Grant"; Console.WriteLine("s1={0}, s2={1}", s1, s2); Swap<String>(ref s1, ref s2); Console.WriteLine("s1={0}, s2={1}", s1, s2); }
Using generic types with methods that take out and ref parameters can be particularly interest- ing because the variable you pass as an out/ref argument must be the same type as the method’s parameter to avoid a potential type safety exploit. This issue related to out/ref parameters is dis- cussed toward the end of the “Passing Parameters by Reference to a Method” section in Chapter 9, “Parameters.” In fact, the Interlocked class’s Exchange and CompareExchange methods offer generic overloads for precisely this reason.1
1 The where clause will be explained in the “Verifiability and Constraints” section later in this chapter. public static class Interlocked { public static T Exchange<T>(ref T location1, T value) where T: class; public static T CompareExchange<T>( ref T location1, T value, T comparand) where T: class; }
Generic Methods and Type Inference For many developers, the C# generic syntax can be confusing with all of its less-than and greater- than signs. To help improve code creation, readability, and maintainability, the C# compiler offers type inference when calling a generic method. Type inference means that the compiler attempts to determine (or infer) the type to use automatically when calling a generic method. Here is some code that demonstrates type inference.
private static void CallingSwapUsingInference() { Int32 n1 = 1, n2 = 2; Swap(ref n1, ref n2);// Calls Swap<Int32>
String s1 = "Aidan"; Object s2 = "Grant"; Swap(ref s1, ref s2);// Error, type can't be inferred }
In this code, notice that the calls to Swap do not specify type arguments in less-than/greater-than signs. In the first call to Swap, the C# compiler was able to infer that n1 and n2 are Int32s, and there- fore, it should call Swap by using an Int32 type argument. When performing type inference, C# uses the variable’s data type, not the actual type of the object referred to by the variable. So in the second call to Swap, C# sees that s1 is a String and s2 is an Object (even though it happens to refer to a String). Because s1 and s2 are variables of dif- ferent data types, the compiler can’t accurately infer the type to use for Swap’s type argument, and it issues the following message: error CS0411: The type arguments for method 'Program. Swap<T>(ref T, ref T)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
A type can define multiple methods with one of its methods taking a specific data type and an- other taking a generic type parameter, as in the following example.
private static void Display(String s) { Console.WriteLine(s); }
private static void Display<T>(T o) { Display(o.ToString()); // Calls Display(String) }
Here are some ways to call the Display method.
Display("Jeff"); // Calls Display(String) Display(123); // Calls Display<T>(T) Display<String>("Aidan"); // Calls Display<T>(T) In the first call, the compiler could actually call either the Display method that takes a String or the generic Display method (replacing T with String). However, the C# compiler always prefers a more explicit match over a generic match, and therefore, it generates a call to the non- generic Display method that takes a String. For the second call, the compiler can’t call the non-generic Display method that takes a String, so it must call the generic Display method. By the way, it is fortunate that the compiler always prefers the more explicit match; if the compiler had preferred the generic method, because the generic Display method calls Display again (but with a String returned by ToString), there would have been infinite recursion. The third call to Display specifies a generic type argument, String. This tells the compiler not to try to infer type arguments but instead to use the type arguments that I explicitly specified. In this case, the compiler also assumes that I must really want to call the generic Display method, so the generic Display will be called. Internally, the generic Display method will call ToString on the passed-in string, which results in a string that is then passed to the non-generic Display method.
Date: 2016-03-03; view: 689
|