How to overcome the C# limitation to overload on generic constraint? - c#

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:
Done() with callback without arguments either on IjQueryPromise or IjQueryPromise<TResult>
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.

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

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

Related

Why do I have to explicitly specify my type arguments for Func parameters?

I am writing a simple Memoize helper that allows caching method results instead of computing them every time. However, when I try to pass a method into Memoize, the compiler can't determine the type arguments. Aren't they obvious from my method signature? Is there a way around this?
Sample code:
using System;
using System.Collections.Concurrent;
public static class Program
{
public static Func<T, V> Memoize<T, V>(Func<T, V> f)
{
var cache = new ConcurrentDictionary<T, V>();
return a => cache.GetOrAdd(a, f);
}
// This is the method I wish to memoize
public static int DoIt(string a) => a.Length;
static void Main()
{
// This line fails to compile (see later for error message)
var cached1 = Memoize(DoIt);
// This works, but is ugly (and doesn't scale to lots of type parameters)
var cached2 = Memoize<string, int>(DoIt);
}
}
Error message:
error CS0411: The type arguments for method 'Program.Memoize<T, V>(Func<T, V>)'
cannot be inferred from the usage. Try specifying the type arguments explicitly.
Isn't DoIt() signature compatible with Func<string, int>?
Yes it is. It's fine to convert it to that specific type, like this for example:
Func<string, int> func = DoIt;
var cachedDoit = Memoize(func);
The problem you're running into is that type inference basically doesn't work particularly well with method group conversions. When you pass DoIt as an argument, that's a method group. In your case it only refers to a single method, but it could refer to multiple methods, with different signatures... and that complicates things.
I often see this come up with LINQ, where I'd like to call foo.Select(SomeMethodGroup), but type inference fails. There's some support for method groups within type inference, but it's not everything we might want it to be.
This isn't a matter of the C# team being lazy... type inference is hugely complicated, and any change is really fraught with danger in terms of backward compatibility. It's in section 7.5.2 of the C# 5 specification if you want to have a look - but frankly that's part of the spec where I get lost really quickly.

How to set a type dynamically into a generic method?

Say you have this generic method:
public static T ConvertValue<T, U>(U value) where U : IConvertible
{
return (T)Convert.ChangeType(value, typeof(T));
}
If I want to call this method inside another generic method. The outer generic method must receive as arguments the actual argument values to set <T, U>(U value) of the inner generic method.
How to achieve this properly, so that I can call OuterGeneric and feed it with the appropriate arguments?
This is just a demonstration of how I need to use it.
public void OuterGeneric<TypeT, TypeU>(TypeT tType, TypeU uType, TypeU valueOfTypeU)
{
// Call of Generic method
TypeT recieverOf_T = ConvertValue<tType, uType>(valueOfTypeU);
}
// Some way to call OuterGeneric. How?
Just call ChangeType directly. You're wrapping the call in a method that requires the type to be specified at compile time, rather than runtime, and then asking how to call it when the type is only known at runtime. You already had a method (ChangeType) that does exactly that.
You don't need method parameters for the generic types in your outer method. You should be able to just use the type parameters like this:
public void OuterGeneric<TypeT, TypeU>(TypeU valueOfTypeU)
{
// Call of Generic method
TypeT recieverOf_T = ConvertValue<TypeT, TypeU>(valueOfTypeU);
}
Then call OuterGeneric the way you would any other generic method.
Your question is a little unclear because you used the term "dynamically." Of course generic parameters must be known at compile time, so if you're looking for a way to use generic methods when only knowing the types at runtime, then you don't actually want to use .NET generics.

How to delegate correct Action<Interface>?

I'm a beginner with C# and can't find any answer for this :
Im trying to delegate Actions with some interface parameter , but push through functions with objects that extend this interface (or class)
// some class extending interface
public class IntEvent : IFace{}
public class MoveEvent : IFace{}
// function i like to use to verify Action
static void setAction(Action<IFace> evt) {
// evt ...
}
// function to delegate as Action
static void evtCheck(IntEvent evt) {
// some func
}
static void evtMove(MoveEvent evt) {
// some func
}
// class {
// call method inside class and delegate this function :
setAction(evtCheck);
setAction(evtMove);
I'm receiving an error that "evtCheck(IntEvent) cannot be converted to Action<IFace>" , even if IntEvent extends the IFace interface .
How should I solve this ? Maybe I have to use Func or Delegate ?
You can't do what you're trying to do - you're expecting covariance, but Action<T> is contravariant on its only type-parameter.
You can't do a method-group conversion from evtCheck to Action<IFace> because evtCheck needs a more specific type (IntEvent) as its argument than the more general IFace type that an Action<IFace> instance expects. If such a conversion were allowed, what would you expect to happen if the delegate were executed with an argument that implements IFace but is not an IntEvent?
I think you should have another look at your design because it looks like there's a flaw there, but if you want, you can create a lambda to force a cast of the argument to the desired type and accept the possibility of an InvalidCastException:
setAction(iFace => evtCheck((IntEvent)iface));
More likely, you might want to make evtCheck accept a more general type or setAction to accept a more specific delegate.
Action<T> is contravariant in T, so methods taking an Action<T> will also accept Action<U> where T is derived from U.
Your IntEvent class, however, implements the IFace interface, which means that if the compiler accepted your setAction() call, it would be possible to call evtCheck() with an argument that is another IFace-derivative and not IntEvent, a runtime error and a clear violation of type safety.
Obligatory Eric Lippert reference: Covariance and contravariance
EDIT: from your description it seems to me that what you really want is differentiation based on the method parameter's type, so e.g. you want a separate action for IntEvent, another one for (a hypothetical) DoubleEvent etc. If this is the case, you are actually after double virtual dispatch, which is not directly supported in C#, but you can simulate it using the Visitor design pattern.

Passing a delegate as a type parameter and using it throws error CS0314

I'm trying to pass a delegate type as a type parameter so that I can then use it as a type parameter later on in the code, like so:
// Definition
private static class Register
{
public static FunctionObject Create<T>(CSharp.Context c, T func)
{
return new IronJS.HostFunction<T>(c.Environment, func, null);
}
}
// Usage
Register.Create<Func<string, IronJS.CommonObject>>(c, this.Require);
However, the C# compiler complains:
The type 'T' cannot be used as type parameter 'a' in the generic type or method
'IronJS.HostFunction<a>'. There is no boxing conversion or type parameter
conversion from 'T' to 'System.Delegate'."
I attempted to fix this by appending "where T : System.Delegate" to the function, however, you can't use System.Delegate as a restriction on type parameters:
Constraint cannot be special class 'System.Delegate'
Does anyone know how to resolve this conflict?
DOESN'T WORK (Argument and return type information is lost during cast):
Delegate d = (Delegate)(object)(T)func;
return new IronJS.HostFunction<Delegate>(c.Environment, d, null);
If you look at https://github.com/fholm/IronJS/blob/master/Src/IronJS/Runtime.fs you'll see:
and [<AllowNullLiteral>] HostFunction<'a when 'a :> Delegate> =
inherit FO
val mutable Delegate : 'a
new (env:Env, delegateFunction, metaData) =
{
inherit FO(env, metaData, env.Maps.Function)
Delegate = delegateFunction
}
In other words, you cannot use C# or VB to write your function because it requires using System.Delegate as a type constraint. I recommend either writing your function in F# or using reflection, like this:
public static FunctionObject Create<T>(CSharp.Context c, T func)
{
// return new IronJS.HostFunction<T>(c.Environment, func, null);
return (FunctionObject) Activator.CreateInstance(
typeof(IronJS.Api.HostFunction<>).MakeGenericType(T),
c.Environment, func, null);
}
#Gabe is completely right, it has to do with the type constraint on the HostFunction<'a> class that is only valid in F# (and not C# or VB).
Have you checked the functions in Native.Utils? It's what we use internally in the runtime to create functions from delegates. Especially the let CreateFunction (env:Env) (length:Nullable<int>) (func:'a when 'a :> Delegate) = function should do exactly what you need.
If CreateFunction doesn't fulfill what you need, open a ticket at http://github.com/fholm/IronJS/issues with what you're missing and how you'd like to see it implemented and we'll get right on it.
As an update to anyone reading this after May 2018:
As of c# 7.3 (.Net Framework 4.7.2), it is now possible to use where T : System.Delegate as a constraint on a generic declaration, which means that the original poster would now be able to do what she was trying to do in c# - without having to resort to building the class with another .Net language.
System.Enum (another sorely-missed constraint in earlier versions of c#) is now available too. There are a couple of other additions as well.
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters#delegate-constraints

Tuple.Create return type

Given
void foo(Tuple<object> t)
{
}
void bar()
{
foo(Tuple.Create("hello"));
}
the c# compiler returns
error CS1502: The best overloaded method match for 'foo(System.Tuple<object>)' has some invalid arguments
error CS1503: Argument 1: cannot convert from 'System.Tuple<string>' to 'System.Tuple<object>'
Adding explicit types to Tuple.Create defeats its purpose. How can I convince the compiler to accept the code?
FWIW, I think C++ doesn't have this problem: http://live.boost.org/doc/libs/1_33_1/libs/tuple/doc/tuple_users_guide.html#constructing_tuples
This is the same generic type covariance issue that comes up daily. It is simply not possible to convert Foo<T> to Foo<SubT> or vice-versa. Starting with .NET 4, it is supported - but only for interfaces and delegates, and by explicitly specifying the generic type parameter as variant by declaring it Foo<out T1>.
You can make the code compile by not using Tuple<object> but using Tuple<T>
void foo<T>(Tuple<T> t)
If you don't want to do that, you simply will need to be explicit with the string to object in the Tuple.Create method.
Tuple.Create<object>("Hello");
Tuple.Create((object)"Hello");
Consider if you could have Tuple<object> and then pass in a Tuple<string>. What if your signature was
void(ref Tuple<object> t)
There's nothing that stops you from writing in that method
t = new Tuple<object>(1);
And now you've just put a 1 in a tuple that only allows strings. Granted, it's a corner case as Tuple is inherently readonly so you need a ref parameter, but it's a problem case, nonetheless.
You are trying to turn a Tuple<string> into a Tuple<object>, which you cannot do - generic variance is only supported for interfaces and delegate. You need to explicitly specify the type arguments to Tuple.Create:
void bar()
{
foo(Tuple.Create<object>("hello"));
}

Categories