2016-07-11 3 views
3

Я следующий список по классу:Получить типы Func из списка выражения <Func>

public class Mapper { 

    public List<Expression> Expressions { get; set; } = new List<Expression>(); 

} 

Я знаю, что все выражения будут:

Expression<Func<InType, OutType>> 

Проблема заключается в том, что InType и OutType варьируется в списке ...

Я попытался определить что-то вроде, но он не компилируется.

Expression<Func<,>> 

Позже мне нужно будет пройти каждое выражение и получить входные и выходные типы функции.

Возможно ли это?

+1

'Список >' это опечатка? –

+0

Если переменные InType и OutType различаются, вам может потребоваться пересмотреть, если вы должны оставить их в том же списке. Else, возможно, InType и OutType могут иметь один и тот же базовый класс или реализовать общий интерфейс. – marsze

+0

Почему бы не объявить список как 'List '? Затем вы также можете получить параметры (т. Е. Тип InType) и возвращаемый тип (т. Е. OutType) программно – bassfader

ответ

1

Это предполагает, что существует только один в-параметра:

public List<LambdaExpression> Expressions { get; set; } = new List<LambdaExpression>(); 

foreach (LambdaExpression expression in Expressions) 
{ 
    var inType = expression.Paramters[0].Type; 
    var outType = expresssion.ReturnType; 
} 

В зависимости от случая использования было бы лучше, чтобы сделать и из типа имеют общий базовый класс или использовать один интерфейс, который сделал бы проверка их типа не требуется.

EDIT:

только LambdaExpressions имеют ReturnType и Parameters свойства. Expression<TDelegate> наследуется от LambdaExpression.

1

Нет, это вообще не возможно.

Тип типа Expression<Func<_,_>> называется более высоким сортным типом. Язык C# не может выразить такие типы.

+1

В качестве комментария я не ответил на вопрос (вопрос) о том, что делать вместо этого. Но то, что вы можете сделать, это получить параметр и вернуть типы из выражения и передать его правильному выражению. Это уродливо, но это сработает. – Martijn

+1

Более высокий тип добрых ... открытый тип ... см. Http://stackoverflow.com/questions/2173107/what-exactly-is-an-open-generic-type-in-net также http: // stackoverflow.com/questions/6607033/c-sharp-language-generics-open-closed-bound-unbound-built – Jay

-2

Обновление: Спасибо всем коллегам и их комментариям. Заменен мой первоначальный ответ.

Первое, что вам абсолютно не нужно Expression, чтобы сделать эту работу, потому что нижняя строка - это локатор типа с завихрением. Взгляните на следующую реализацию:

public class FuncMapper 
{ 
    Dictionary<Type, Delegate> _funcs = new Dictionary<Type, Delegate>(); 

    public void Register<TIn, TOut>(Func<TIn, TOut> func) 
    { 
     _funcs.Add(func.GetType(), func); 
    } 

    public TOut Execute<TIn, TOut>(TIn param) 
    { 
     return ((Func<TIn, TOut>)_funcs[typeof(Func<TIn, TOut>)])(param); 
    } 
} 

Затем, используя это просто:

class Program 
{ 
    static void Main(string[] args) 
    { 
     FuncMapper funcMapper = new FuncMapper(); 

     funcMapper.Register<string, string>(DoString); 
     funcMapper.Register<int, int>(DoInt); 

     Console.WriteLine("Value: {0}", funcMapper.Execute<string, string>("Test")); 
     Console.WriteLine("Value: {0}", funcMapper.Execute<int, int>(10)); 

     Console.Read(); 
    } 

    static string DoString(string param) 
    { 
     return param; 
    } 

    static int DoInt(int param) 
    { 
     return param; 
    } 
} 

Update2: Если вам действительно нужно выражение для какой-либо причине, вы можете изменить Register метод следующим образом:

public void Register<TIn, TOut>(Expression<Func<TIn, TOut>> expression) 
{ 
    Func<TIn, TOut> func = expression.Compile(); 

    _funcs.Add(func.GetType(), func); 
} 

А потом называют это так:

funcMapper.Register<string, string>((param) => DoString(param)); 
funcMapper.Register<int, int>((param) => DoInt(param)); 
+0

Это не путь, потому что func может иметь до 15 параметров –

+2

Из вопроса, кажется, что только один параметр если нет, может быть, вопрос должен отражать это. –

+2

«Выражение» не является ковариантным, поэтому вы не можете рассматривать, например, выражение «Func >» как выражение > '. – Servy

1

Вы можете использовать следующий код

  List<Expression> list = new List<Expression>(); 

      var typePairs = list.OfType<LambdaExpression>().Select(x => 
       new 
       { 
        inType = x.Parameters[0].Type, 
        outType = x.ReturnType 
       }); 
+0

В чем разница между использованием выражения LambdaExpression и выражением? –

+0

@MiguelMoura Есть много выражений, которые не являются лямбдами. – Servy

+0

Выражение типа @MiguelMoura не имеет параметров и ReturnType. Это от LambdaExpression –

0

Все типы происходят от объекта ... легко использовать объект, но потеряет типобезопасность вы хотите.

Возможно определить интерфейс IParameter, который определяет семантику вашей логики, например.InType, OutType, HasInputValue, HasOutputValue.

Используйте реализацию интерфейса для решения проблемы.

Возможно, определите класс Number или NumberResult, который реализует этот интерфейс и использует его или интерфейс как тип, открытый в списке.

0

Вот полный пример, который может помочь вам

class Program 
{ 
    static void Main(string[] args) 
    { 
     List<Expression> expressions = new List<Expression>(); 
     Expression <Func<string,int>> func = a => a.Length; 

     expressions.Add(func); 
     foreach (var expression in expressions) 
     { 
      LambdaExpression lmdExpression = expression as LambdaExpression; 
      if (lmdExpression!=null) 
      {  
       //get all params 
       List<string> paramsList = new List<string>(); 
       foreach (var parameterExpression in lmdExpression.Parameters) 
       {      paramsList.Add(parameterExpression.Type.ToString()); 
       } 
       var returnedType = lmdExpression.ReturnType.ToString(); 
       //and here you can use a big switch to invoke your needed expression     
      } 
     }   
     Console.ReadLine();  
    } 
} 

Почему с помощью Expression и LambdaExpression?

Поскольку LambdaExpression является абстрактным классом, из которого происходит Expression<TDelegate> и так как вы не знаете, какой тип Func<T,T> вы используете, вы должны использовать базовый класс

+0

Зачем использовать выражение и LambdaExpression? –

+0

Я обновил свой ответ –

1

Я надеюсь, что вы готовы сделать некоторые размышления , Это невозможно, используя только типы времени компиляции, но с отражением вы можете извлекать типы во время выполнения.

Недостатками являются:

  • Вы должны убедиться, что список содержит только Expression<Func<In, Out>> с, а не другие выражения типа. (Не могут быть проверены во время компиляции)
  • Это медленно

Пример кода:

class Program 
{ 
    public static void ConsumeExpressions(List<LambdaExpression> exprs) 
    { 
     var consumerMethod = typeof(Program).GetMethod("ConsumeExpression", BindingFlags.Public | BindingFlags.Static); 
     foreach (var expr in exprs) 
     { 
      var inType = expr.Parameters[0].Type; 
      var outType = expr.ReturnType; 
      var genericMethod = consumerMethod.MakeGenericMethod(inType, outType); 
      genericMethod.Invoke(null, new object[] { expr }); 
     } 
    } 

    public static void ConsumeExpression<TInType, TOutType>(Expression<Func<TInType, TOutType>> expr) 
    { 
     Console.WriteLine("in: {0}, out: {1}, {2}", typeof(TInType).Name, typeof(TOutType).Name, expr); 
    } 

    static void Main(string[] args) 
    { 
     ConsumeExpressions(new List<LambdaExpression> 
     { 
      (Expression<Func<int, string>>)(i => ""), 
      (Expression<Func<string, int>>)(s => 0) 
     }); 
    } 
} 

EDIT

Предлагайте использованием LambdaExpression S на основе пост Брахим, поэтому отражение часть получает короче и яснее.