Is there a way to force methods of non-generic classes to use the
most specific overload when the calling class is generic?
Normally, the overload resolution takes place at compile-time. Since the constraints on T
, namely where T: struct, IComparable, IComparable<T>, IConvertible, IEquatable<T>, IFormattable
, cannot point to any other overload than the one taking in object
, that's the overload getting called.
If you are willing to use dynamic
, the overload resolution will happen at run-time. Simply cast the argument to dynamic
, like this:
public void Stuff()
{
ExternalAPI.Read((dynamic)buffer);
}
The bad thing about this is that it's slower because overload resolution has to set in when your program runs. But if T
is one of byte
, int
, and so on, the corresponding overload with byte[]
, int[]
etc. will be called. If T
is something not supported, and if the object
overload does not exist in ExternalAPI
, this will fail at run-time, throwing an exception, but not until the Stuff
method runs.
Another solution is to equip your Foo<T>
class with a delegate to hold the correct method. It could be like:
T[] buffer;
readonly Action<T[]> readMethod;
public void Stuff()
{
readMethod(buffer);
}
//constructor
public Foo(Action<T[]> readMethod)
{
this.readMethod = readMethod;
}
But then people would have to instantiate your class like this:
new Foo<byte>(ExternalAPI.Read)
The right overload would be selected compile-time each place where people created instances of Foo<>
. You could add checks in you instance constructor that readMethod
is indeed a unicast delegate representing a method called Read
defined by typeof(ExternalAPI)
.
A third solution is to make the readMethod
field static
, and include a static constructor that initializes readMethod
. That would look ugly, but the static constructor would only run once for each type (byte
, int
, etc.) you used. The static constructor could throw an exception if people used a wrong T
. Here's what the static constructor may look like:
static Foo()
{
if (typeof(T) == typeof(byte))
readMethod = (Action<T[]>)(Delegate)(Action<byte[]>)ExternalAPI.Read;
else if (typeof(T) == typeof(int))
readMethod = (Action<T[]>)(Delegate)(Action<int[]>)ExternalAPI.Read;
else if (typeof(T) == typeof(float))
readMethod = (Action<T[]>)(Delegate)(Action<float[]>)ExternalAPI.Read;
else if (typeof(T) == typeof(double))
readMethod = (Action<T[]>)(Delegate)(Action<double[]>)ExternalAPI.Read;
else
throw new Exception("The type parameter T can't be " + typeof(T));
}
Edit: Inspired by the first comment below, here's an attempt to make the static constructor use reflection instead:
static Foo()
{
var methInfo = typeof(ExternalAPI).GetMethod("Read", new[] { typeof(T[]), });
if (methInfo == null)
throw new Exception("ExternalAPI has no suitable method for " + typeof(T[]));
readMethod = (Action<T[]>)Delegate.CreateDelegate(typeof(Action<T[]>), methInfo);
}