Question

I have an interesting question for you all.

public Func<T, int> GetLambdaFor<T>()
{
   // ....
}

public void SetLambdaForAnyType(Func<?, int> lambda)
{
   // ....
}

In the code sample above I would like to find out if it is at all possible to specify a substitutable Generic lambda expression in C# using the SET method while retrieving a specifically typed Generic lambda expression using the GET method.

So let me give an example (ofcourse it features broken C#, I would like to find out if it is possible and if so, how to do it):

MyClass.SetLambdaForAnyType<?>((a) => new SomeValueGenerator<?>(a).GetValue());
int generateValue = MyClass.GetLambdaFor<string>()("blah");
generateValue = MyClass.GetLambdaFor<DateTime>()(DateTime.Now);
// etc...

I hope it makes more sense now. Any ideas? If it is not possible, any alternative possibilities?

Was it helpful?

Solution

A lambda expression cannot introduce generic parameters, there is no syntax for it. You will have to use generic methods instead of lambdas, and use reflection to construct closed generic methods for particular types, like this (code not tested):

public Func<T, int> GetLambdaFor<T> ()
{
    return GetLambdaFor<T> (m_method, m_target) ;
}

public void SetLambdaForAnyType (Func<object, int> lambda)
{
   if (lambda == null) throw new ArgumentNullException ("lambda") ;

   // delegate complicated signature checks to CLR
   try
   {
       var definition = lambda.Method.GetGenericMethodDefinition () ;
       GetLambdaFor<Dummy> (definition, lambda.Target) ;
       m_method = definition ;
       m_target = lambda.Target ;
   }
   catch (Exception e)
   {
       throw new ArgumentException ("lambda", e) ;
   }
}

private sealed class Dummy {}
private MethodInfo m_method ;
private object     m_target ;
static Func<T, int> GetLambdaFor<T> (MethodInfo method, object target)
{
    return (Func<T, int>) Delegate.CreateDelegate (typeof (Func<T, int>),
        target, method.MakeGenericMethod (typeof (T))) ;
}

Then you use it like this

// in your class
private static int GetValueFromGenerator<T> (T a)
{
    return new SomeValueGenerator<T> (a).GetValue () ;
}

MyClass.SetLambdaForAnyType (GetValueFromGenerator) ;

Finally, if your 'lambdas' will always be of this form, you might want to introduce an interface for GetValue and instead of creating generic methods on-the-fly, create generic types for these generators, activate them and cast to IGetValue.

OTHER TIPS

You can do it, sure. You lose type safety within this type, but we know it'll work correctly anyway from an external point of view:

public class Foo
{
    private Dictionary<Type, Delegate> dictionary =
        new Dictionary<Type, Delegate>();

    public Func<T, int> GetLambdaFor<T>()
    {
        return dictionary[typeof(T)] as Func<T, int>;
    }
    public void SetLambdaFor<T>(Func<T, int> func)
    {
        dictionary[typeof(T)] = func;
    }
}

Of course, you may want to use TryGetValue from the dictionary, if you want to handle the case that there is no entry for that type.

Another option would be to just use a thread static field:

public class Foo2<T>
{
    [ThreadStatic]
    public Func<T, int> Generator { get; set; }
}

Then it's as simple as:

Foo2<string>.Generator = s => 5;
int result = Foo2<string>.Generator("asdf");
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top