Как сгенерировать делегат или лямбда-выражение

Я хочу создать метод, который возвращает Func‹>. Внутри этого метода я должен создать делегат или лямбда-выражение, которое точно соответствует возвращаемому типу.

В целом это должно выглядеть так:

// I have a resolve method that will be called inside my missing method
// This is it's signature:
object Resolve( params object[] args);

// This is how I use it:
var barFactory = ( Func<IBar> )MissingMethod( typeof( IBar ) );
var bar = barFactory.Invoke();

// or - with one string argument:
var fooFactory = ( Func<string, IFoo> )MissingMethod( typeof( IFoo ), typeof( string ) );
var foo = fooFactory.Invoke( "argument for foo" );

Внутри MissingMethod() это должно выглядеть так:

object MissingMethod( Type returnType, params Type[] argTypes )
{
  // Create the type of Func<> based on the passed returnType and the argTypes
  var funcType = typeof(Func<,...,>).MakeGenericType( ... )

  // Here I have to use the Resolve() method and cast the lambda to the correct type
  return (cast to funcType)( (arg1, arg2) => Resolve( arg1, arg2 ) );
}

Я думаю, что единственный способ получить мой MissingMethod() — это использовать Reflection.emit.

Знаете ли вы хорошие ресурсы или руководства по созданию лямбда-выражения или делегата?

Видите ли вы другое возможное решение этой проблемы?

EDIT:
Вот сценарий того, чего я хочу достичь:

static void Main()
{
  var container = new Container();
  container.Register<Foo>();
  container.Register<ConsumerClass>();

  var consumerClass = Container.Resolve<ConsumerClass>();
}

class Foo()
{
  public Foo( string argument ) {}
}

class ConsumerClass
{
  public ConsumerClass( [Inject] Func<string, Foo> factory )
  {
    var foo1 = factory.Invoke( "first foo" );
    var foo2 = factory.Invoke( "another foo" );
    // ...
  }
}

Я пытаюсь реализовать метод Container и Resolve(). Я знаю, что зарегистрирован тип "Foo". И я знаю, что его конструктору нужна строка для вызова.

Когда мне нужно разрешить тип «ConsumerClass», я вижу, что он хочет ввести Func. Это не совсем то, что может предоставить мой контейнер, потому что обычно он предоставляет единичные экземпляры для Foo следующим образом:

Container.Resolve<Foo>( "argument" );

Но, тем не менее, контейнер должен также обеспечивать Func. В нем есть вся необходимая информация.

Но теперь я застрял в создании этого связанного Func‹,>. И помните, что это может быть и Func‹,,,>. Поэтому я ищу решение, которое может создавать моих делегатов на лету. Они должны быть приводимы к точному связанному типу.

EDIT:
Я не уверен, как лучше описать это... Я пытаюсь сделать что-то вроде это. Но я не хочу проходить мимо цели. Вместо

delegate void object LateBoundMethod( object target, object[] arguments );

мой делегат должен выглядеть

delegate void object LateBoundMethod( object[] arguments );

и цель предоставляется как поле экземпляра. Взяв и «улучшив» решение Марка, я получаю:

private Delegate CreateDelegate( Type returnType, Type[] parameterTypes )
{
  m_Type = returnType;

  var i = 0;
  var param = Array.ConvertAll( parameterTypes, arg => Expression.Parameter( arg, "arg" + i++ ) );
  var asObj = Array.ConvertAll( param, p => Expression.Convert( p, typeof( object ) ) );
  var argsArray = Expression.NewArrayInit( typeof( object ), asObj );

  var callEx = Expression.Call( null, typeof( FuncFactory ).GetMethod( "Resolve" ), argsArray );
  var body = Expression.Convert( callEx, returnType );

  var ret = Expression.Lambda( body, param ).Compile();
  return ret;
}

private readonly Container m_Container;
private Type m_Type;

public object Resolve( params object[] args )
{
  return m_Container.Resolve( m_Type, args );
}

Но это неполное. Метод Resolve() больше не является статическим (потому что ему нужны два поля экземпляра) и не может быть вызван. Так что проблема вот в чем

var callEx = Expression.Call( null, typeof( FuncFactory ).GetMethod( "Resolve" ), argsArray );

Вместо того, чтобы передавать null в качестве первого аргумента, я думаю, мне нужна ссылка на «это». Как мне это сделать?


person tanascius    schedule 02.06.2009    source источник
comment
Должен сказать, что я не уверен на 100%, что понимаю, что вы пытаетесь сделать, но разве создание универсального метода MissingMethod не поможет вам на полпути к решению ваших проблем? Или это не вариант?   -  person kastermester    schedule 02.06.2009
comment
Кроме того, я понимаю тот факт, что вам придется кодировать его несколько раз, чтобы охватить все возможные дженерики Func‹›, но, как упоминал Марк, вам все равно придется жестко кодировать это.   -  person kastermester    schedule 02.06.2009
comment
Что касается редактирования, я до сих пор не понимаю, каковы правила... что бы вы хотели, чтобы он вводил в [Inject]? И почему?   -  person Marc Gravell    schedule 03.06.2009


Ответы (1)


Первая проблема заключается в том, что Func<...> не существует — вам нужно кодировать Func<>, Func<,>, Func<,,>, Func<,,,> отдельно.

В настоящее время; Я понимаю код, но я не уверен, какой сценарий вы пытаетесь решить... не могли бы вы пояснить? Наверняка есть варианты получше...

Если это так сложно, как кажется, пользовательский Expression, вероятно, является наиболее подходящим вариантом (гораздо проще, чем Reflection.Emit).


Это работает, например...

static Delegate MissingFunc(Type result, params Type[] args)
{
    int i = 0;
    var param = Array.ConvertAll(args,
        arg => Expression.Parameter(arg, "arg" + i++));
    var asObj = Array.ConvertAll(param,
        p => Expression.Convert(p, typeof(object)));
    var argsArray = Expression.NewArrayInit(typeof(object), asObj);
    var body = Expression.Convert(Expression.Call(
                null, typeof(Program).GetMethod("Resolve"),
                argsArray), result);
    return Expression.Lambda(body, param).Compile();
}
static void Main()
{
    var func2 = MissingFunc(typeof(string), typeof(int), typeof(float));
}
public static object Resolve( params object[] args) {
    throw new NotImplementedException();
}
person Marc Gravell    schedule 02.06.2009
comment
Спасибо за ответ. Вы правы насчет Func‹...› - я должен ограничить количество принятых аргументов четырьмя. Я хочу отделить свой код от метода Resolve() (который предоставляется IoC-контейнером). Вместо этого я хочу иметь возможность создавать маленькие фабрики (передаваемые как аргументы Func‹›), которые позволяют объекту создавать больше экземпляров одного типа, косвенно используя IoC-контейнер. - person tanascius; 02.06.2009
comment
Спасибо... Я новичок в выражениях. Можете ли вы дать мне подсказку, как сделать Resolve() методом экземпляра с объектом подписи Resolve(Type, params object)? Я все еще застрял :/ - person tanascius; 02.06.2009
comment
Извините, я должен был уйти на поезд; Я могу подробно остановиться на этой области, но сначала повторю свой вопрос: каков здесь сценарий? Я мог бы подойти к этому как минимум тремя способами (и ни один из них не является Reflection.Emit)... если это метод instance... что это за экземпляр of? Возможно, вы могли бы показать немного больше полузавершенных. И если вы еще в чем-то не уверены, не стесняйтесь снять это как ответ - я не обижусь. Отметьте его завершенным только тогда, когда вы довольны тем, что вопрос решен (я не уверен, что это так...) - person Marc Gravell; 03.06.2009
comment
Я принял ваш ответ, потому что уверен, что он дает мне полезную информацию :) Мне просто нужно выяснить, как вызвать объект метода экземпляра Resolve (Type, params object) с помощью выражений. Я отредактировал свой пост - надеюсь, он разъясняет, что я хочу сделать... - person tanascius; 03.06.2009