Generic Methods

Generic Methods

A generic method can declare method-specific generic parameters. These parameters can be used in the method header or body. An open method has type parameters, which are nonspecific. A closed method has type arguments, whereas specific types are substituted for type parameters. For a generic method, the type parameters are listed after the function header. The type parameter list is enclosed in anchor brackets, whereas each parameter is delimited with commas.

This is a prototypical generic method:

using System;

namespace Donis.CSharpBook{

    public class Starter{
        public static void Main(){
            ZClass.MethodA<int>(20);
        }
    }

    public class ZClass{
        public static void MethodA<T>(T param) {
            Console.WriteLine(param.GetType().ToString());
        }
    }
}

When calling a generic method, provide the type arguments as a substitute for type parameters. There is an alternate syntax to calling a generic method, called type inference, in which the type argument is inferred from the actual method parameters. The type argument can then be omitted. Basically, the generic method is called similarly to any method. The benefit is ease of use. However, type inference disguises the true nature of the call, which could be relevant when debugging an application.

In the previous code, ZClass.MethodA is a generic method. The following statement calls MethodA using type inference:

            ZClass.MethodA(20);

This is the syntax of a generic method:

  • attributes accessibility modifiers returntype methodname <parameterlist>

  • (argumentlist) where parameter:constraintlist

  • { method body }

Overloaded Methods

Generic methods can be overloaded, which creates an interesting dilemma. Is a method ambiguous based on extrapolation alone? In the following code, MethodA is overloaded:

      public void MethodA(T arg) {
      }
      public void MethodA(U arg) {
    }

Both functions have a single type parameter. The type parameters are different, but each parameter could be anything, including both parameters being identical. This makes certain renditions of MethodA ambiguous. For example, they both could have a single integer parameter. However, the C# compiler is concerned with actual ambiguity and does not highlight, as an error or warning, potential ambiguousness related to overloading generic methods. A compile error is manifested when MethodA is called, if ever, in an ambiguous manner. This is a potential land mine for developers of libraries. Test every permutation of type parameters to predict and avoid potential ambiguousness for clients of a library.

In the following code, MethodA is called ambiguously. Interestingly, MethodA is known to be ambiguous at the generic type instantiation. However, the compiler error occurs on the next statement at the call site.

using System;

namespace Donis.CSharpBook{

    public class Starter{
        public static void Main(){

            ZClass<int, int> obj=new ZClass<int, int>();
            obj.MethodA(5); // ambiguous error
        }
    }

    public class ZClass<T, U> {
        public void MethodA(T arg) {
            Console.WriteLine("ZClass.MethodA(T arg)");
        }

        public void MethodA(U arg) {
            Console.WriteLine("ZClass.MethodA(U arg)");
        }

        public void MethodA() {
            Console.WriteLine("ZClass.MethodA()");
        }
    }
}

Generic methods can overload nongeneric methods. If the combination of a generic and nongeneric method is ambiguous, the nongeneric method is called. The compiler prefers nongeneric methods over generic methods of the same signature.

The following code contains a generic and nongeneric MethodA. When MethodA is called with an integer parameter, the call would otherwise be ambiguous. Because the ambiguous methods are generic and nongeneric methods, the nongeneric MethodA is simply called. Then MethodA is called with a double, which is not ambiguous, and the generic MethodA is called appropriately.

using System;

namespace Donis.CSharpBook{

    public class Starter{
        public static void Main(){
            ZClass<int> obj1=new ZClass<int>();
            obj1.MethodA(5);
            ZClass<double> obj2=new ZClass<double>();
            obj2.MethodA(5.0);
        }
    }

    public class ZClass<T> {
        public void MethodA(T arg) {
            Console.WriteLine("ZClass.MethodA(T arg)");
        }

        public void MethodA(int arg) {
            Console.WriteLine("ZClass.MethodA(int arg)");
        }

        public void MethodA() {

            Console.WriteLine("ZClass.MethodA()");
        }
    }
}

This Reference for Generic Types

Generic types, similar to any type, have a this reference, which is a reference to the current object and the same type of that object. If the object is an XClass instance, the this reference is also of the XClass type. The this reference is used implicitly to refer to instance members and explicitly as function returns and parameters. The this reference of a generic type is associated with the closed constructed type, which is normally defined at generic type instantiation.

The following code displays the type of a this reference. The following is a this reference of a generic type, which is Donis.CSharpBook.ZClass`1[System.Int32]. From the type, you know that the closed constructed type has a single parameter that is a 32-bit integer.

using System;

namespace Donis.CSharpBook{

    public class Starter{
        public static void Main(){
            ZClass<int> obj=new ZClass<int>();
            obj.MethodA();
        }
    }
    
    class ZClass<T> {
        public T MethodA() {
           T var=default(T);
           Console.WriteLine(this.GetType().ToString());
           return var;
        }
    }
}