You can make a object parameter:
public void DoSomething(object arg) {
Or you could do what I prefer and make a general method:
public void DoSomething<T>(T arg) {
The general approach has two main advantages, and I will give examples of why they are useful:
- Even if you do not explicitly specify the
arg type, you still have access to it. - You can add restrictions on the types you want to allow.
Conversely, the object approach has some important drawbacks:
- Since you are handling
arg as an object , you can do what you can do with any object. - If you pass the value type as the
object parameter, the variable will be boxed , which means a performance hit. This is not a huge blow, but if you call DoSomething several thousand times in a row, you can start to feel it.
General and typical limitations
Adding a type constraint to the general method allows you to restrict the method so that it accepts only certain types. Why is this useful? Because, although you do not know or do not care about which particular type you are working with, you now know something about it, and you can use this information.
Consider the following setup:
public interface IAnimal { void Move(); } public class Duck : IAnimal { public void Move() { Console.WriteLine("Flying"); } } public class Fish : IAnimal { public void Move() { Console.WriteLine("Swimming"); } } public class Ant : IAnimal { public void Move() { Console.WriteLine("Walking"); } }
Since we have an IAnimal interface, we can write generic methods that target any IAnimal implementation:
public class Program { static void DoMove<T>(T animal) where T : IAnimal { animal.Move(); } public static void Main(string[] args) { Duck duck = new Duck(); Fish fish = new Fish(); Ant ant = new Ant(); DoMove<Duck>(duck); DoMove<Fish>(fish); DoMove<Ant>(ant); } }
Run it: http://rextester.com/GOF1761
When we write the DoMove method, we donโt care whether its parameter is animal Duck , a Fish , a Ant or something else. All we care about is animal.Move() . Since we used the where T : IAnimal , the compiler knows everything we need to know:
- The variable
animal is of type T - Regardless of
T , it implements IAnimal . - Everything that implements
IAnimal has a Move() method. - Therefore, we can safely call
animal.Move() .
(By the way, yes, we could just write DoMove as static void DoMove(IAnimal animal) , but that's another discussion.)
Type inference (and some of its consequences)
Ok, but let's take one more step. In many cases, you can call common methods without specifying their type parameters. This is called the type of output , and, in addition to saving a certain set of text, it can be useful when performing the same operation on objects of different types.
public static void Main(string[] args) { IAnimal[] animals = new IAnimal[] { new Duck(), new Fish(), new Ant() }; foreach (IAnimal animal in animals) { DoMove(animal); } }
Run it: http://rextester.com/OVKIA12317
You only need to write the DoMove<T> method DoMove<T> , and you can call it on any type of IAnimal without specifying a more specific type. The corresponding version of Move will be called every time, because DoMove<T> can infer which type to use for T When you call DoMove(duck) , .NET understands that you really mean DoMove<Duck>(duck) , which then calls the Move method in the Duck class.