2016-03-31 41 views
1

Şu anda, belirli dize alanlarının DB uzunluğunu almak için kullandığım aşağıdaki yönteme sahibim (her özelliğin MetaDataFieldAttribute adlı bir özniteliği vardır). Güzel çalışıyor gibi görünüyor, ancak özellik adıyla yazılan 'dize'. Bir ifade ağacı burada çalışır mı?

 public static void Main(string[] args) 
     { 
      ... 
      obj.PropertyName = str.TrimIfRequired(obj, "PropertyName"); 
     } 

     public static string TrimIfRequired(this string str, object obj, string property) 
     { 
      var pi = obj.GetType().GetProperty(property); 
      var attribute = pi.GetCustomAttributes<MetaDataFieldAttribute>().FirstOrDefault(); 
      if (attribute == null) return str; 

      int length = attribute.Precision; 
      return str.Length > length ? str.Substring(0, length) : str; 
     } 

Ben

ifade ağaçlarının oldukça temel bir anlayışa sahip ve benim ilk düşünce ben yöntemin amacı/parametre kuvvetle sadece dize geçerek yerine yazdığınız yapabilir olmasıdır. Mümkün mü?

cevap

1

Evet, yalnızca özellik adları için güçlü yazım yapmak için İfadeleri burada kullanabilirsiniz, bu nedenle özellik adını dize olarak yazmak yerine, özellik erişimi ile ifade kullanabilirsiniz ve kod, özel özellik ifadesi için MemberInfo öğesini alır ve buradan özellik alacak. Bu nedenle, propertyName değerini ileride değiştirirseniz, bu bölüm güçlü yazılacağı için derlenmez. - Burada

.NetFiddle üzerinde numuneyi çalışıyor burada https://dotnetfiddle.net/qYOfhE

Ve kod kendisidir:

using System; 
using System.Linq; 
using System.Linq.Expressions; 

public class Program 
{ 
    public static void Main(string[] args) 
    { 
     var str = "TestTestTest"; 

     var propertyName = str.TrimIfRequired<Customer>((c) => c.FirstName); 
     var propertyNameValue = str.TrimIfRequired<Customer>((c) => c.Age); 
     Console.WriteLine(propertyName); 
     Console.WriteLine(propertyNameValue); 
    } 
} 

public static class Extensions 
{ 
    public static string TrimIfRequired<T>(this string str, Expression<Func<T, object>> expr) 
    { 
     var me = expr.Body as MemberExpression; 
     if (me == null && expr.Body is UnaryExpression) 
     { 
      me = ((UnaryExpression) expr.Body).Operand as MemberExpression; 
     } 

     if (me == null) 
     { 
      throw new ArgumentException("Invalid expression. It should be MemberExpression"); 
     } 

     var pi = me.Member; 
     var attribute = pi.GetCustomAttributes(typeof(MetaDataFieldAttribute), false).FirstOrDefault() as MetaDataFieldAttribute; 
     if (attribute == null) return str; 

     int length = attribute.Precision; 
     return str.Length > length ? str.Substring(0, length) : str; 
    } 
} 

public class Customer 
{ 
    [MetaDataField(Precision = 6)] 
    public string FirstName { get; set; } 

    [MetaDataField(Precision = 2)] 
    public int Age { get; set; } 
} 


public class MetaDataFieldAttribute : Attribute 
{ 
    public int Precision { get; set; } 
} 
+0

Fantastik, teşekkürler :) – Chris

+2

biliyorum UnaryExpression ol. 2 tür parametrelerle genel yöntem (ör. TrimIfRequired (bu dize str, Expression > propertyAccess) veya aslında "yakalama" sınıfını inbetween olarak tanıtacağınızdan, özellik türünü yazmanız gerekmez) veya UnaryExpression ile sarmayı da halledin! – MBoros

+0

@MBoros evet, tamamen haklısınız. Basit bir çözümle gittim ve yanıtımı "UnaryExpression" olarak güncelledik, çünkü gerçek özellik erişimini yapmıyoruz ve kutulama burada bir sorun değil, ancak yeni 'TProp' eklenmesi kullanımı biraz zorlaştırabilir –

0

İşe ama olur Main içeride 'obj' değişken iyi bilinen tam olması durumunda yazın. object yeterli değildir, çünkü belirtilen tipte object verilen herhangi bir ifade ağacı herhangi bir gerçek üye görmeyecektir. Ama zaten PropertyName'a atadığınız için, o değişkene sahip olduğunuz anlaşılıyor. Bu gibi kullanılabilir

public static string TrimIfRequired<T>(this string str, T obj, Expression<Func<T,object>> propexpr) 

:

obj.PropertyName = str.TrimIfRequired(obj, o => o.PropertyName); 

veya

public static string TrimIfRequired<T>(this string str, Expression<Func<T,object>> propexpr) 

bu gibi kullanılabilir (olmadan nesne

Sizin TrimIfRequired da yani, değiştirilmesi gerekir

örnek, ancak tür açıkça stat ile Sadece obj vermek ve C# da otomatik olarak T alır ve lambda immediatelly kullanabilirsiniz beri ed)

obj.PropertyName = str.TrimIfRequired<MyType>(o => PropertyName); 

İlki, güzeldir. Ancak, obj değişkenine sahip olmanız gerekir. İkinci örnek, beton türünü bildiğinizde, ancak geçecek herhangi bir değişken veya örneğiniz olmadığında yararlıdır. Ancak bu nadirdir.

Her iki durumda da, analiz etmeniz gereken bir Expression<Func<T,..>> alırsınız. Çoğunlukla, MemberExpression (özellik, alan) alırsınız ve ondan, özellik adını ayıklamanız gerekir. Ancak, kullanıcının lambda'da herhangi bir şey yazabileceğini unutmayın - derleyici için o => { foreach(..) {..}; return 1;}, o => o.PropertyName kadar iyidir, bu yüzden doğru ifadenin geçip geçmediğini ve en azından bazı minimal geliştiricilerin işlenmesini sağlayıp sağlamadığını tespit etmelisiniz.

BTW1. Func yerine, Eylem'i de kullanabilirsiniz, ancak bu bağlamda kullanımda tuhaf olduğunu düşünüyorum.

hemen değerini yazar

1) yardımcı (get: etkili o mülk üzerinde tanımlı bazı kısıtlamasına bir nesnenin özellik uzunluğu kısaltmak istediğiniz beri

Ancak, ben araçları aşağıdaki daha yararlı veya ergonomik olacağını düşünmek başka bir ifadeyle olarak PropertyInfo, ardından yardımcı this obj üzerinde çağrıldı gerçeğini kullanmak ve) PropertyInfo.SetValue çağırmak için kullanabilirsiniz

obj.TrimIfRequired(o => o.PropertyName); 

özellikleri yürür ve tüm özelliklerine tüm kısıtlamaları (bildiğimiz geçerlidir 2) yardımcı nesnenin çalışma zamanı Türü, Propertie'yi tarayabiliriz s, onların Öznitelikler'i olsun, biz expr.Body olacak (onlar üyesi erişim itiraz bir dönüştürmek olacak gibi bu değer türü parametreleri için başarısız olur) kırparak ihtiyacı tam olarak ne

obj.TrimAllIfRequired(); 
İlgili konular