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);
}