Лучшая практика создания общих делегатов и получения доступа к свойствам - PullRequest
5 голосов
/ 07 марта 2011

Я хочу создать делегатов для доступа к свойствам различных объектов, не зная их заранее.

У меня есть следующие определения

public delegate T MyMethod<K, T>(K data);

public static MyMethod<K, T> CreatePropertyGetter<K, T>(PropertyInfo property)
{       
   MethodInfo mi = property.DeclaringType.GetMethod("get_" + property.Name);        
return (MyMethod<K, T>)Delegate.CreateDelegate(typeof(MyMethod<K, T>), mi);
}

, где T может быть десятичным, строковым, datetime или int

У меня есть некоторый инициализирующий код, который будет создавать делегаты MyMethod на основе отраженных свойств моего объекта следующим образом:

foreach (PropertyInfo property in entityType.GetProperties())
{               
    switch (property.PropertyType.Name)
    {
        case "System.Decimal":
            return CreatePropertyGetter<T, decimal>(property);
        case "System.DateTime":
            return CreatePropertyGetter<T, DateTime>(property);
        case "System.String":
            return CreatePropertyGetter<T, DateTime>(property);
    }
}

Есть ли лучший способ

  1. создавать свойства геттеров?
  2. перечислять поддерживаемые типы свойств, жестко закодированные как строки?

EDIT:

Меня беспокоит производительность, так как эти делегаты будут вызываться часто (тикающий сценарий), поэтому любое приведение будет замедлять его. Хотя желательно более элегантное решение, производительность по-прежнему является моей главной задачей

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

Ответы [ 3 ]

3 голосов
/ 07 марта 2011

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

Пример использования:

Action<object> compatibleExecute =
    DelegateHelper.CreateCompatibleDelegate<Action<object>>( owner, method );

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

UPDATE:

Я не проверял это, но в вашем случае вы можете попробовать следующее:

Func<object> getter =
    DelegateHelper.CreateCompatibleDelegate<Func<object>>( owner, method );

method должен быть установлен для получателя, который вы получили. owner должен быть установлен на экземпляр вашего объекта. Если вы хотите разрешить делегату передать владельца в качестве аргумента, вам придется скорректировать код. Пример приведен Владимиром Матвеевым в качестве комментария к статье Джона Скита .

static Func<T, object, object> MagicMethod<T>(MethodInfo method)    
{    
    var parameter = method.GetParameters().Single();    
    var instance = Expression.Parameter(typeof (T), "instance");
    var argument = Expression.Parameter(typeof (object), "argument");

    var methodCall = Expression.Call(
        instance,
        method,
        Expression.Convert(argument, parameter.ParameterType)
        );

    return Expression.Lambda<Func<T, object, object>>(
        Expression.Convert(methodCall, typeof (object)),
        instance, argument
        ).Compile();
   }
2 голосов
/ 07 марта 2011

Ознакомьтесь с этой статьей Джона Скита:

http://msmvps.com/blogs/jon_skeet/archive/2008/08/09/making-reflection-fly-and-exploring-delegates.aspx

Этот метод динамически определяет тип возвращаемого значения получателя.

public static class DelegateCreator
{
    //-----------------------------------------------------------------------
    public static Func<T, object> GetMethod<T>( PropertyInfo propertyInfo ) 
        where T : class
    {
        MethodInfo method = propertyInfo.GetGetMethod( true );
        if( method == null )
        {
            string msg = String.Format( "Property '{0}' does not have a getter.", propertyInfo.Name );
            throw new Exception( msg );
        }

        // First fetch the generic form
        MethodInfo genericHelper = typeof( DelegateCreator ).GetMethod( "CreateGetHelper",
             BindingFlags.Static | BindingFlags.NonPublic );


        // Now supply the type arguments
        MethodInfo constructedHelper = genericHelper.MakeGenericMethod
             ( typeof( T ), method.ReturnType );

        // Now call it. The null argument is because it's a static method.
        object ret = constructedHelper.Invoke( null, new object[] { method } );

        // Cast the result to the right kind of delegate and return it
        return (Func<T, object>)ret;
    }

    //-----------------------------------------------------------------------
    static Func<TTarget, object> CreateGetHelper<TTarget, TReturn>( MethodInfo method )
        where TTarget : class
    {
        // Convert the slow MethodInfo into a fast, strongly typed, open delegate
        Func<TTarget, TReturn> func = (Func<TTarget, TReturn>)Delegate.CreateDelegate
             ( typeof( Func<TTarget, TReturn> ), method );

        // Now create a more weakly typed delegate which will call the strongly typed one
        Func<TTarget, object> ret = ( TTarget target ) => func( target );
        return ret;
    }

}

Используется так:

PropertyInfo pi = typeof( Employee ).GetProperty( "LastName" );

Action<Employee, object> getMethod = DelegateCreator.SetMethod<Employee>( pi );
string lastName = getMethod( employee );
2 голосов
/ 07 марта 2011

Лучший путь:

  1. Использование Expression<TDelegate>.Например:

    открытый статический класс PropertyExpressionHelper {

    public static TProperty GetProperty<T,TProperty>(this T obj, Expression<Func<T,TProperty>> getPropertyExpression)
    {
        if(obj == null)
        {
            throw new ArgumentNullException("obj");
        }
        if(getPropertyExpression==null)
        {
            throw new ArgumentNullException("getPropertyExpression");
        }
        var memberExpression = getPropertyExpression.Body as MemberExpression;
        bool memberExpressionIsInvalidProperty = memberExpression == null ||
                                                 !(memberExpression.Member is PropertyInfo &&
                                                   memberExpression.Expression.Type == typeof (T));
        if(memberExpressionIsInvalidProperty)
        {
            throw new ArgumentNullException("getPropertyExpression", "Not a valid property expression.");
        }
        return (TProperty)(memberExpression.Member as PropertyInfo).GetValue(obj, null);
    }
    

    }

  2. Чтобы получить перечисление типов всех свойств в типе, сделайте это: typeof(T).GetProperties().Select(x=>x.PropertyType).Distinct();

Посмотрите на исходный код, написанный кем-то для переключателя типов C #, доступный в в этом посте .Я думаю, что это может иметь то, что вы ищете.

...