0

Let's have the following code (type-safe stubs for jQuery):

public interface IjQueryPromise { }

public interface IjQueryPromise<TResult> : IjQueryPromise { }


public static class jQueryPromiseEx
{
    public static T Done<T>(this T t, params Action[] doneCallbacks)
        where T : IjQueryPromise { return t; } 

    public static T Done<T, TResult>(this T t, params Action<TResult>[] doneCallbacks)
        where T : IjQueryPromise<TResult> { return t; }
}

Using this code, I am trying to allow calling:

  1. Done() with callback without arguments either on IjQueryPromise or IjQueryPromise<TResult>
  2. Done() with callback with one argument of type TResult on IjQueryPromise<TResult>.

But since C# (v5.0) is unable to overload on generic constraints, passing lambda like:

(r) => DoSomething(r) 

will produce compiler error:

Delegate 'System.Action' does not take 1 arguments.

I am implementing the methods as extension methods to return the type of the object on which the function is called.

The only idea what I have is automatically inline the code inside the interfaces and all their implementations.

Is it possible solve this in C# some better way?

Or does already exist a tool that do the inlining?

EDIT: I am designing the API for developers. I know that I can cast to delegate, but I do not want the developers (users of the API) to be forced to do the cast every time.

TN.
  • 18,874
  • 30
  • 99
  • 157
  • what about creating delegates and casting to them? – Blau Sep 09 '14 at 15:58
  • possible duplicate of ['Delegate 'System.Action' does not take 0 arguments.' Is this a C# compiler bug (lambdas + two projects)?](http://stackoverflow.com/questions/4466859/delegate-system-action-does-not-take-0-arguments-is-this-a-c-sharp-compiler) – Nahum Sep 09 '14 at 16:00
  • @Blau and NahumLitvin please see the edit. – TN. Sep 09 '14 at 16:03
  • @NahumLitvin The question you referred is about C# bug that has been fixed in C# 5 (I am using C# 5.) – TN. Sep 09 '14 at 16:07
  • if you don't want your users to cast... I think the other option is passing rguments as object and do the cast by yourself in your code – Blau Sep 09 '14 at 16:07
  • @Blau this will not be type-safe. They could then pass anything. – TN. Sep 09 '14 at 16:07
  • 1
    I fear that you'll have to choose among the bad and the evil... – Blau Sep 09 '14 at 16:09

2 Answers2

1

This isn't an issue with overload resolution.

It has nothing to do with a conflict with the other overload.

This is an issue with type inference. Your second overload won't be able to infer the type of TResult if you don't indicate the type of the parameter for the lambda, or the type of the entire delegate.

This is easy enough to see by simply removing the first overload from your application entirely. All that will do is change the error message from what you're getting to one saying that type inference has failed.

One way that you can both allow type inference and actually not provide any of the type information elsewhere (i.e typing the type out for the lambda parameter) would be to go down to just one generic argument:

public static IjQueryPromise<T> Done<T>(this IjQueryPromise<T> t,
    params Action<T>[] doneCallbacks)
{ return t; }
Servy
  • 202,030
  • 26
  • 332
  • 449
  • +1 Thank you for the hint with the type argument. Finally (for now), I've decided to remove the first overload. – TN. Sep 10 '14 at 12:36
1

The problem is that c# cannot infer the type of the argument in the anonymous method.

it will work if you declare the argument type this way;

IjQueryPromise<int> promise; 
promise.Done( (int r) => DoSomething(r) ); // This should work as expected 
Blau
  • 5,742
  • 1
  • 18
  • 27