2016-06-25 2 views
1

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

public class Entity 
{ 
    public static readonly EntitySchema Schema = new EntitySchema(); 
} 

public abstract class BaseSchema 
{ 
    public abstract string Name {get;} 
} 

public class EntitySchema : BaseSchema 
{ 
    public override string Name => "Schema"; 
} 

Теперь я хочу, чтобы получить доступ к EntitySchema.Name от метода, который ничего не знаю о сущности (не может получить доступ к статическому полю) не знает.

Я мог бы сделать это с отражением:

static BaseSchema GetSchema<T>() 
{ 
    var pr = typeof(T).GetField("Schema"); 
    var schema = pr.GetValue(null); 
    return schema as BaseSchema; 
} 

, но по сравнению с прямым вызовом Entity.Schema.Name от версии отражения 50x медленнее.

Есть ли способ конвертировать версию отражения в Дерево выражений и предварительно скомпилировать вызов?

ответ

2

Уверен, что вы можете это сделать, но для этого требуется, чтобы в этом случае для полного определения поля (EntitySchema global::Entity.Schema) для определения фактического значения. Отражение дает вам это определение. Вы должны были бы сделать делегат для каждого типа, а именно:

public static class SchemaGetter 
{ 
    private static readonly Dictionary<object, Func<BaseSchema>> _lookup = new Dictionary<object, Func<BaseSchema>>(); 

    public static BaseSchema Get<T>() 
    { 
     Func<BaseSchema> action; 

     if(!_lookup.TryGetValue(typeof(T), out action)) 
     { 
      action = MakeDelegate<T>(); 

      _lookup.Add(typeof(T), action); 
     } 

     return action();    
    } 

    private static Func<BaseSchema> MakeDelegate<T>() 
    { 
     // We did this before already... 

     FieldInfo field = typeof(T).GetField("Schema", BindingFlags.Public | BindingFlags.Static); 
     var fieldExpression = Expression.Field(null, field); 

     var lambda = Expression.Lambda<Func<BaseSchema>>(fieldExpression); 

     return lambda.Compile(); 
    } 
} 

Вслед за BaseSchema schema = SchemaGetter.Get<Entity>(), чтобы получить фактическую схему.

Возможно, его достаточно для кэширования результата, полученного у вас из первоначальной реализации GetSchema<T>() (оберните его в поиск по словарю). Я думаю, что в этом случае это должно быть, так как поле все равно статично.

+0

Спасибо, что сработало! Запуск скомпилированной версии var f = MakeDelegate () теперь составляет около 44 мс для вызовов 10mio вместо 1250 мс! –