3

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
    }
}
bleevo
  • 1,637
  • 2
  • 18
  • 30
  • I guess i don't really understand what the question is... how to use a delegate? – chills42 Aug 07 '09 at 19:06
  • Question is how can I can I infer the parameter type of T1 through T4, copy the code into a program and make it compile. – bleevo Aug 08 '09 at 00:32

5 Answers5

3

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.

Noldorin
  • 144,213
  • 56
  • 264
  • 302
  • Although not a solution I think you have confirmed what I believed that this isnt possible, I am not thinking a T4 template and lots of method overloads is the only way :( – bleevo Aug 14 '09 at 05:48
  • Yeah, unless I am greatly mistaken, there is no solution to your problem (not a direct one, at least). Could you maybe clarify why exactly you must have the type inferred rather than explicitly specifying it? – Noldorin Aug 14 '09 at 11:36
0

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).

Community
  • 1
  • 1
Sam Harwell
  • 97,721
  • 20
  • 209
  • 280
  • The ultimate goal here was to pass in the method name for ASP.NET MVC for generating urls by looking up the route based on the type of the controller and name of the method without going ("MyMethod") but rather (c=>c.MyMethod) where the type of parameters that MyMethod takes isnt important or its return type for that matter just the name of it. – bleevo Jul 10 '10 at 04:08
0

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.

Akash Kava
  • 39,066
  • 20
  • 121
  • 167
  • Regarding: object k = t.ReturnEmpty(); compiler could return an error on this one stating there is no way to implicitly define which method needs to be called here. But that would give a significant additional complexity. Also type inference wont work for apparent reasons... – Ray Aug 11 '09 at 12:42
  • This question has nothing to do with method resolution, but rather generic type parameter resolution, which is done quite separately by the C# compiler. – Noldorin Aug 11 '09 at 12:43
  • @Ray, @Noldorin I am aware of the errors and whether you call generic type parameter resolution or method reoslution, the rules of resolution needs can not be ambigious, thats what I am trying to explain that this can never be acheived by an alternate example. – Akash Kava Aug 11 '09 at 12:55
  • sorry for typo, i mean rules of resolution can not be ambigious – Akash Kava Aug 11 '09 at 12:57
0

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); }
Donald Byrd
  • 7,668
  • 4
  • 33
  • 50
  • As you will see in the question this method isnt acceptable, as I would have to do this for every type and combination of types. – bleevo Aug 14 '09 at 05:46
  • Sorry, I misunderstood. I should have re-read the question before answering. I don't think there is a solution that fits your problem constraints. – Donald Byrd Aug 14 '09 at 12:45
0

Its looking more and more like this cannot be done :(

bleevo
  • 1,637
  • 2
  • 18
  • 30
  • After further research there is no simple way to support this method, I will explore using a T4 template. – bleevo Aug 17 '09 at 00:37
  • Thats exactly what Noldorin answered, why not accept his more detailed answer? – nawfal Apr 27 '13 at 14:57