Reputation: 1667
Below is some code that is used to pass around a reference to a method that contains strings as parameters, the purpose of this question is around using generics to negate the need to define the actual type!
Impossible<ExampleSource, string>.Example(c => c.NonString); //does not work
Impossible<ExampleSource, string>.Example<int>(c => c.NonString); //does work
The idea here is to make the first "NonString" call work without having to define the parameter type or declare a new function in Impossible that takes Func<int, TResult>.
public static void Example(Expression<Func<TSource, Func<int, TResult>>> function)
{ Process(function as MethodCallExpression); } //invalid solution...
In Java this could be achieved using Func<?, TResult>
public class Impossible<TSource, TResult>
{
public static void Example(Expression<Func<TSource, Func<TResult>>> function)
{ Process(function as MethodCallExpression); }
public static void Example(Expression<Func<TSource, Func<string, TResult>>> function)
{ Process(function as MethodCallExpression); }
public static void Example(Expression<Func<TSource, Func<string, string, TResult>>> function)
{ Process(function as MethodCallExpression); }
public static void Example<T1>(Expression<Func<TSource, Func<T1, TResult>>> function)
{ Process(function as MethodCallExpression); }
public static void Example<T1, T2>(Expression<Func<TSource, Func<T1, T2, TResult>>> function)
{ Process(function as MethodCallExpression); }
private static void Process(MethodCallExpression exp)
{
if (exp == null) return;
Console.WriteLine(exp.Method.Name);
}
}
public class ExampleSource
{
public string NoParams() { return ""; }
public string OneParam(string one) { return ""; }
public string TwoParams(string one, string two) { return ""; }
public string NonString(int i) { return ""; }
}
public class Consumer
{
public void Argh()
{
Impossible<ExampleSource, string>.Example(c => c.NoParams);
Impossible<ExampleSource, string>.Example(c => c.OneParam);
Impossible<ExampleSource, string>.Example(c => c.TwoParams);
Impossible<ExampleSource, string>.Example<int>(c => c.NonString);
Impossible<ExampleSource, string>.Example(c => c.NonString); //MAKE THIS WORK
}
}
Upvotes: 3
Views: 741
Reputation: 7788
Can you add this to the Impossible<> class to accept the <int, TResult>
call??
public static void Example(Expression<Func<TSource, Func<int, TResult>>> function)
{ Process(function as MethodCallExpression); }
Upvotes: 0
Reputation: 147461
The simple answer is no, this is not possible in the latest version of C#. The inference of generic type parameters for method invocation is just not clever enough to realise that method you pass returns an int
. Hence, you are forced to specify it yourself.
The long answer can be found by reading through section 14.5.5.1
of the C# Language Specification. Although it is far from direct on this specific case, it does implicitly state that the sort of type inference for which you are hoping is not possible. Read through that section and the corresponding text in section 25
, and you should know all there is to know about generic type parameters and their inference.
Hope that helps. Let me know if you need any clarification on specific parts.
Upvotes: 3
Reputation: 39956
There is no way to do this, because Method Resolution always works on combination of return type and parameters but not only on return type. The reason I just posted in other post, here is the problem. The reason why C# does not offer method resolution by only different return type is following...
public class Test{
public int DoMethod(){ return 2; }
public string DoMethod() { return "Name"; }
}
Test t;
int n = t.DoMethod(); // 1st method
string txt = t.DoMethod(); // 2nd method
object x = t.DoMethod(); // DOOMED ... which one??
If you notice, in last statement, the programming language has no correct solution of which method to choose.
This is the exact same reason the following code does not work...
public class Test{
public T ReturnEmpty<T>() { return default(T); }
public T ReturnEmpty<T>(T x) { return x; }
}
Test t = new Test();
int n = t.ReturnEmpty(); <--- does not work..
the reson check next statement...
object k = t.ReturnEmpty(); <--- which one...
int l = t.ReturnEmpty<int>(); <--- this is correct....
string m = t.ReturnEmpty("Do Test"); <-- this is correct...
So once again, same solution, resolving method based only on return type is an abigious solution for any compiler. Programming languages are different from spoken languages because they are designed to always give "One Execution Way" only, no ambiguity and no context dependency. Every statement works exactly independent of the context.
However you can argue that you have correct return type, but that may not always be the case, compilers need strong rules to compile and generate code.
Upvotes: 0
Reputation: 99989
The most explicit, strongest typing I could come up with is listed in the question part of this post:
Why is there not a fieldof
or methodof
operator in C#?
To top that, I'd need to know a bit more about exactly what your goals are (bigger picture here).
Upvotes: 0