2015-07-09 177 views
1

說的返回類型我有一個Expression<Func<T,object>>是否有可能動態地改變根據Type變量返回類型是像Expression<Func<T,int>>改變的表達<func<>>

我有下面的類:

public class ImportCheck<T> { 

    public int id { get; set; } 
    public string Name { get; set; } 
    public Type Type { get; set; } 
    public bool Required { get; set; } 
    public int? MinLength { get; set; } 
    public int? MaxLength { get; set; } 
    public string Value { get; set; } 
    public Expression<Func<T, object>> AssociatedProperty { get; set; } 
} 

我有一個List<ImportCheck<Contact>>我循環,併爲每個設置Contact對象屬性(屬性都是不同的類型)。爲了使我能夠設置嵌套對象的屬性,我需要結果類型與目標類型相同。 如果聯繫人的所有屬性都是int,那麼我現在擁有的工作將會很好,這是因爲我有一個不同類型的列表導致我很頭疼。

這是如何設置的子屬性:

private static Action<M, R> MakeSet<M, R>(Expression<Func<M, R>> fetcherExp) { 
      if (fetcherExp.Body.NodeType != ExpressionType.MemberAccess) { 
       throw new ArgumentException(
        "This should be a member getter", 
        "fetcherExp"); 
      } 

      // Input model 
      var model = fetcherExp.Parameters[0]; 
      // Input value to set 
      var value = Expression.Variable(typeof(R), "v"); 
      // Member access 
      var member = fetcherExp.Body; 
      // We turn the access into an assignation to the input value 
      var assignation = Expression.Assign(member, value); 
      // We wrap the action into a lambda expression with parameters 
      var assignLambda = Expression.Lambda<Action<M, R>>(assignation, model, value); 

      return assignLambda.Compile(); 
     } 

這被稱爲然後像MakeSet(member)(target,value)其中構件是Expression<Func<T,object>>target是對象和value是將屬性設置爲值。

+0

你可以在你試圖做更詳細的?特別是包括不完全符合你想要的示例代碼? –

+0

你能舉一個試圖設置子屬性的例子嗎?特別是,這是明確的逐案代碼,還是以某種方式推廣? –

+0

@EamonNerbonne增加了一些關於我如何設置屬性的細節。由於我的表達結果是一個對象,這是通過一個UnaryExpression而不是一個MemberExpression這是什麼導致我的問題 – Gazeth

回答

1

當然;您可以創建一個新的表達式樹,並將其從對象轉換爲您想要的任何類型(當然,該類型無法保留) - 您可以在新表達式中使用舊錶達式樹的一部分(即整個lambda體)樹。但是,即使您創建了這樣的事情,請注意,如果您想要靜態地表示表達式的類型 - 例如, Expression<Func<T,int>>你將需要靜態地知道類型。泛型會起作用 - 但運行時變量不是。

有幾個問題你的方法:

  • 您認爲fetcherExp.Body是一個成員訪問,例如obj.TheProperty。但是,如果表達式的類型爲Expression<Func<T,object>>,則任何值類型屬性都將表示爲「Convert(obj.TheProperty)」。
  • 你假設有一個setter對應於getter。
  • 您認爲Type屬性是正確的。

我建議你以不同的方式解決這個問題。我建議你從一個精確輸入的Expression<Func<T,TProperty>> - 或者甚至只是一個PropertyInfo開始,並生成鍵入的獲得者和設置者,而不是處理不正確輸入的Expression<Func<T,object>>對象並嘗試生成setter(和getter?)。一旦你有一個Func<T,TProperty> getterAction<T,TProperty> setter,你可以很容易地包裝那些產生更少的具體行動和funcs中,自從協方差類型屬性的干將

public static Action<T,object> UntypeSetter<T,TProperty>(Action<T,TProperty> typedSetter) => 
    (o, val) => typedSetter(o, (TProperty)val); 

類似的方法是干將有用的(但這只是問題意味着參考類型獲得者可以全部投射到Action<T,object>)。

如果您絕對需要極高的運行時性能,您可以使用Expression<...> s完成相同的包裝技巧,並將嵌套調用嵌入到typedSetter,但請注意,您並未贏得太多的勝利;對於大多數應用來說,一個和兩個代表呼叫之間的差異不太可能是重要的。

TL; DR:不要使用Expression<Func<T,object>>作爲無類型屬性的中間表示;這樣做會拋棄對創建getter/setter有用的類型信息。相反,使用鍵入的表達式Expression<Func<T,TProperty>>可輕鬆生成無類型的getter和setter,並通過那些作爲您的中間表示。

+0

謝謝,你有沒有一個例子 – Gazeth

2

請看以下例子:

public class ReturnTypeVisitor<TSource, TReturnValue> : ExpressionVisitor{ 

     protected override Expression VisitLambda<T>(Expression<T> node) 
     { 
      var delegateType = typeof(Func<,>).MakeGenericType(typeof(TSource), typeof(TReturnValue)); 
      return Expression.Lambda(delegateType, Visit(node.Body), node.Parameters); 
     } 

     protected override Expression VisitMember(MemberExpression node) 
     { 
      if (node.Member.DeclaringType == typeof(TSource)) 
      { 
       return Expression.Property(Visit(node.Expression), node.Member.Name); 
      } 
      return base.VisitMember(node); 
     } 
} 

用法:

public class Foo{ 
    public Bar Bar { get; set; } 
} 

public class Bar { } 

Expression<Func<Foo, object>> expression = p => p.Bar; 
Expression<Func<Foo, Bar>> stronglyTypedReturnValue =(Expression<Func<Foo, Bar>>) new ReturnTypeVisitor<Foo, Bar>().Visit(expression); 
相關問題