2016-09-20 2 views
1

Im написание кода для передачи данных моих объектов ORM в набор данных. Поскольку я не хочу писать специальный код для каждого типа, определяющий, какие свойства нужно записать, я в настоящее время использую отражение (вызывая GetProperties по типу сущностей, создавая DataTable для этого типа, а затем вызывающий GetValue для каждого Propertyinfo для каждый объект). Статус кво: он работает, но он медленный.Создание функций для извлечения значений свойств, полученных с помощью отражения

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

/// <summary> 
    /// creates a func that will return the value of the given property 
    /// </summary> 
    /// <typeparam name="T">type of the entity</typeparam> 
    /// <param name="propertyInfo">the property to get the value from</param> 
    /// <returns>a function accepting an instance of T and returning the value of the property</returns> 
    private Func<T, object> CreateGetPropertyFunc<T>(PropertyInfo propertyInfo) 
    {   
    MethodInfo getMethod = propertyInfo.GetGetMethod(); 
    return (Func<T, object>)Delegate.CreateDelegate(typeof(Func<T, object>), getMethod);   
    } 

Это мои единичные тесты:

[TestMethod] 
    public void TestGenerateDelegate() 
    { 
    var employee = new Employee 
    {    
     Id = 1, 
     Name = "TestEmployee",    
    }; 

    Func<Employee, object> getIdValueFunc = CreateGetPropertyFunc<Employee>(typeof(Employee).GetProperty("Id")); 
    Assert.AreEqual(1, getIdValueFunc(employee)); 
    } 

    [TestMethod] 
    public void TestGenerateDelegateName() 
    { 
     var employee = new Employee 
     { 
      Name = "Test" 
     }; 

     Func<Employee, object> getNameValueFunc = CreateGetPropertyFunc<Employee>(typeof(Employee).GetProperty("Name")); 
     Assert.AreEqual("Test", getNameValueFunc(employee)); 
    } 

Когда я называю первый, в ArgumentException с сообщением «Исключение при связывании с целевым методом» (перевод, может быть другой текст). Второе испытание проходит вместо этого.

Я уверен, что я неправильно обрабатываю этот метод CreateDelegate. Может ли кто-нибудь указать мне в правильном направлении, пожалуйста?

Update:

Как PetSerAI statet, это, кажется, проблема с дисперсией, значения примитивных типов не могут быть возвращены в качестве объекта через CreateDelegate ...

+1

Делегат возвращаемый тип дисперсии не работает с типами значений. Вы не можете привязать метод, возвращающий 'int', чтобы делегировать возвращаемый объект. – PetSerAl

+0

Вы правы, работая с свойством Name, но не с Id. Как жаль! – Udontknow

ответ

1

Вы можете использовать деревья выражений для динамического создания делегата, которые упоминаются указанное свойство:

private Func<T, object> CreateGetPropertyFunc<T>(PropertyInfo propertyInfo) { 
    ParameterExpression p = Expression.Parameter(typeof(T)); 
    return Expression.Lambda<Func<T, object>>(
     Expression.Convert(Expression.Property(p, propertyInfo), typeof(object)), 
     p 
    ).Compile(); 
} 
+0

Отлично, это работает. Большое спасибо! – Udontknow

2

Вы можете просто вызвать ваш getMethod:

private Func<T, object> CreateGetPropertyFunc<T>(PropertyInfo propertyInfo) 
{ 
    MethodInfo getMethod = propertyInfo.GetGetMethod(); 
    return o => getMethod.Invoke(o, BindingFlags.Default, null, null, CultureInfo.CurrentCulture); 
} 
+0

Это может быть что-то, что мы, немцы, называем «опасным полузнанием», но не вызывается через методinfo медленнее по сравнению с прямым вызовом? Это то, чего я хочу избежать ... – Udontknow

0

Почему бы не пройти общий путь и не дать компилятору создать делегат для вас?

public static class Ext 
{ 
    public static Func<T, TProp> CreateGetPropertyFunc<T, TProp>(this T obj, Func<T, TProp> func) 
    { 
     return func; 
    } 
} 

Тогда:

var getterForId = employee.CreateGetPropertyFunc(x => x.Id); 
int result = getterForId(employee); 
// result can now be 'int' and doesn't have to be 'object' 

Если вы не имеете реальный экземпляр T заранее, или просто не хотят разгибания метод подход:

public Func<T, object> CreateGetPropertyFunc<T>(Func<T, object> func) 
{ 
    return func; 
} 

Затем:

var getterForId = CreateGetPropertyFunc<Employee>(x => x.Id); 
object result = getterForId(employee); 
// result must be 'object' (unless explicitly casted) 

(бывший лучше с точки зрения производительности, так как ценностные типы как int не будут боксировал, и сохраняется безопасность типа)

+0

Может быть, я неправильно понимаю вас. Я не хочу писать все эти строки для всех тех свойств сущности, которые у меня есть; Я хочу динамически получить все свойства типа через отражение и создать методы быстрого доступа для извлечения значений свойств. Какой смысл писать лямбда «x => x.Id», чтобы получить ... идентификатор? – Udontknow

+0

@Udontknow, «Точка написания лямбды» заключается в том, что компилятор переводит его в желаемый 'Func <>', и вам не нужно использовать Reflection. Кроме того, ** 1) ** это лучше с точки зрения производительности и ** 2) ** он короче как в определении, так и в реализации, и, самое главное, ** 3) ** он сохраняет безопасность типов. – haim770

+0

Я понимаю преимущества лямбда, и я чувствую, что я не ясно описал свою цель. У меня есть один метод расширения в DataSet и вы хотите иметь возможность передавать данные любого объекта любого типа (!) В экземпляры DataTable. Как написать lambdas для свойств, которые я не знаю во время разработки? Thats, где возникает отражение. :-) – Udontknow

1

CreateDelegate(Type, MethodInfo) связывается со статическим методом (у вас нет статического метода, поэтому вы получаете сообщение об ошибке)

вы можете использовать только Delegate.CreateDelegate для методов экземпляра этой версии: CreateDelegate(Type, Object, MethodInfo)

Создает делегат указанного типа, который представляет заданный статический или экземпляр с указанным первым аргументом.

https://msdn.microsoft.com/en-us/library/system.delegate.createdelegate(v=vs.110).aspx

Согласно комментарий PetSerAl, вы должны пройти «нулевой» как «первый аргумент» для создания «открытого делегата», в которую вы бы передать экземпляр.

// In this case, the delegate has one more 
    // argument than the instance method; this argument comes 
    // at the beginning, and represents the hidden instance 
    // argument of the instance method. Use delegate type D1 
    // with instance method M1. 
    // 
    d1 = (D1) Delegate.CreateDelegate(typeof(D1), null, mi1); 

    // An instance of C must be passed in each time the 
    // delegate is invoked. 
    // 
    d1(c1, "Hello, World!"); 
+0

'CreateDelegate (Type, MethodInfo)' может создавать делегат от нестатического метода: 'Delegate.CreateDelegate (typeof (Func )), typeof (object) .GetMethod (« ToString »)). [Docs] (https://msdn.microsoft.com/library/53cz7sc6.aspx#Anchor_2) говорит: * В .NET Framework версии 2.0 этот метод перегрузки также может создавать делегаты метода открытого экземпляра; то есть делегаты, которые явно предоставляют скрытый первый аргумент методов экземпляра. * – PetSerAl

+0

Спасибо, я этого не осознал - поэтому ему нужно использовать другую перегрузку. Я отредактирую свой ответ соответствующим образом. –

Смежные вопросы