Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Syntax sugar for double-generic function

I have the following function in c#:

bool Handle<TCommandHandler, TModel>(TModel model) where TCommandHandler : ICommandHandler<TModel> {
    // ...
    _container.Resolve<TCommandHandler>();
    // ...
}

Since TModel is clear from a function parameter I want some way to not specify its type when calling a function. Ideally I want to call it like:

Handle<MyCommandHandler>(model);

Since this is probably impossible, I came up with the following:

HandleTemp<TModel> Handle<TModel>(TModel model) {
    return new HandleTemp<TModel>(model);
}

public class HandleTemp<TModel> {
    private TModel _model;
    public HandleTemp(TModel model) { _model = model;}

    public bool With<TCommandHandler>() where TCommandHandler : ICommandHandler<TModel> {
    }
}

So I'm now calling it like:

Handle(model).With<MyCommandHandler>();

Are there other possibilities? Did I make something completely wrong with my solution?

like image 947
Shaddix Avatar asked Oct 04 '11 07:10

Shaddix


2 Answers

No, your analysis and solution look about right. Indeed, generic type inference can work only on an all-or-nothing basis. If there are some generic parameters that can't be inferred, all must be explicitly stated. Personally I'd quite like a way to say "you worry about these parameters, I'll tell you this one", but... that doesn't exist.

The only other option is to add an artificial extra regular parameter to allow it to infer the generic parameter - a bit yucky.

One other option: challenge the assumption that generics are needed here. For example, could it just be a Type instance? Would:

bool Handle<TModel>(TModel model, Type type)...
...
Handle(model, typeof(MyCommandHandler));

work, for example? I can't answer this directly, as I don't know the particulars of your _container.Resolve<TCommandHandler>(); method, as to whether that could be adjusted to take a Type rather than a <T>.

like image 64
Marc Gravell Avatar answered Oct 17 '22 20:10

Marc Gravell


All the C# compiler needs is a demonstration of the type in the arguments, so instead of attempting to place it in the generic arguments (at the usage site) make something that lets you provide an argument that helps the compiler identify that type. To make it less confusing, here is an example:

// Your classes/interfaces.
class Container
{
    public static T Resolve<T>()
    {
        Console.WriteLine("Resolving {0}", typeof(T).FullName);
        return default(T);
    }
}
interface ICommandHandler<TModel>
{
    void DoSomething();
}

// An implemented ICommandHandler.
public class WackyCommandHandler : ICommandHandler<string>
{
    public void DoSomething() { }
}

// Used to help the C# compiler identify types.
public static class Identify
{
    public static TypeIdentity<TType> TheType<TType>()
    {
        return null; // You don't actually need an instance.
    }
}
public sealed class TypeIdentity<TType>
{
    private TypeIdentity() { }
}

// Your method
static bool Handle<TCommandHandler, TModel>(TModel model, TypeIdentity<TCommandHandler> handler)
    where TCommandHandler : ICommandHandler<TModel>
{
    var item = Container.Resolve<TCommandHandler>();
    return true;
}

// And the usage site:
var a = "hello";
Handle(a, Identify.TheType<WackyCommandHandler>());
Console.ReadLine();
like image 31
Jonathan Dickinson Avatar answered Oct 17 '22 20:10

Jonathan Dickinson