简体   繁体   中英

Force C# compiler to use non-generic method overload with Linq Expression parameter

I have overloaded methods, one generic and one non-generic. The two methods both receive a Linq Expression as single parameter:

public void Test(Expression<Action<char>> expr) {}

public void Test<T>(Expression<Func<char, T>> expr) {}

Now consider the following invocation:

var sb = new StringBuilder();
Test(c => sb.Append(c));

The compiler will pick the generic method since the Append() method does (unfortunately) return a StringBuilder . However, in my case I absolutely need the non-generic method to be called.

The following workaround shows that there is no type issue with the code (the non-generic call would be perfectly valid):

Expression<Action<char>> expr = c => sb.Append(c);
Test(expr);

However, I'd prefer not to declare a variable with an explicit type and instead somehow get the compiler to pick the non-generic method (just like I could tell it to use the generic method with explicit type parameters).

You can play with this at SharpLab.io .

This may seem like a workaround (because it is), but you can used a named parameter to clarify which method you are calling.

static public void Test(Expression<Action<char>> action) 
{
    Console.WriteLine("Test()");
}

static public void Test<T>(Expression<Func<char, T>> func) 
{
    Console.WriteLine("Test<T>()");
}

When you want the non-generic version, just provide the parameter name action: in the argument list.

static public void Main()
{
    var sb = new StringBuilder();
    Test(action: c => sb.Append(c) );
    Test(func: c => sb.Append(c) );
}

Output:

Test()
Test<T>()

This might be easier to use than writing out the expression cast.

Fiddle

You can use an empty method to swallow the return value of sb.Append. I wouldn't call this a workaround, since it just makes the compiler work normally, but it isn't totally clean and pretty either.

static public void NoValue(object value) {}

static public void Test(Expression<Action<char>> action) 
{
    Console.WriteLine("Test()");
}

static public void Test<T>(Expression<Func<char, T>> func) 
{
    Console.WriteLine("Test<T>()");
}

When you wrap the output in NoValue, the compiler correctly sees this as an Action, not a function.

static public void Main()
{
    var sb = new StringBuilder();
    Test(c => NoValue(sb.Append(c)) );
    Test(c => sb.Append(c) );
}

Output:

Test()
Test<T>()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM