2009-07-30 12 views
35

Chciałbym uzyskać nazwę nieruchomości, gdy jestem w niej za pomocą mechanizmu odbicia. Czy to możliwe?Jak uzyskać nazwę bieżącej właściwości za pomocą odbicia?

Aktualizacja: mam kod jak poniżej:

public CarType Car 
    { 
     get { return (Wheel) this["Wheel"];} 
     set { this["Wheel"] = value; } 
    } 

A ponieważ potrzebuję więcej właściwości takich jak to chciałbym zrobić coś takiego:

public CarType Car 
    { 
     get { return (Wheel) this[GetThisPropertyName()];} 
     set { this[GetThisPropertyName()] = value; } 
    } 
+1

+1 Ja też zastanawiałem się o tym podczas wdrażania System.Configuration.ApplicationSettingsBase –

Odpowiedz

47

Ponieważ właściwości są naprawdę tylko metody można to zrobić i oczyścić get_ powrócił:

class Program 
    { 
     static void Main(string[] args) 
     { 
      Program p = new Program(); 
      var x = p.Something; 
      Console.ReadLine(); 
     } 

     public string Something 
     { 
      get 
      { 
       return MethodBase.GetCurrentMethod().Name; 
      } 
     } 
    } 

Jeśli profil wydajności należy znaleźć MethodBase.GetCurrentMethod() jest mile szybciej niż StackFrame. W .NET 1.1 będziesz mieć również problemy ze StackFrame w trybie wydania (z pamięci Myślę, że znalazłem to 3x szybciej).

Powiedział, że jestem pewien, że problem z wydajnością nie spowoduje zbyt dużego problemu - chociaż interesującą dyskusję na temat powolnego spowolnienia StackFrame może być found here.

Przypuszczam, że inną opcją, jeśli obawiasz się o wydajność, byłoby utworzenie kodu Visual Studio IntelliSense kodu, który tworzy właściwość dla Ciebie, a także tworzy ciąg odpowiadający nazwie właściwości.

+0

Interesujące - nie wiedziałem o GetCurrentMethod() – BlueMonkMN

+0

Czy jest coś podobnego do tego dla 'Właściwości'? –

0

Tak, jest!

string test = "test string"; 
Type type = test.GetType(); 

PropertyInfo[] propInfos = type.GetProperties(); 
for (int i = 0; i < propInfos.Length; i++) 
{ 
    PropertyInfo pi = (PropertyInfo)propInfos.GetValue(i); 
    string propName = pi.Name; 
} 
0

Spróbuj użyć System.Diagnostics.StackTrace, aby zastanowić się nad stosem wywołań. Właściwość powinna znajdować się gdzieś w stosie wywołań (prawdopodobnie u góry, jeśli wywołujesz ją bezpośrednio z kodu nieruchomości).

5

Zamiast tego należy użyć metody MethodBase.GetCurrentMethod()!

Odbicie jest używane do pracy z typami, których nie można wykonać w czasie kompilacji. Uzyskanie nazwy dostępnego obiektu właściwości można określić podczas kompilacji, więc prawdopodobnie nie powinieneś używać do tego celu refleksji.

Można jednak użyć nazwy metody dostępowej ze stosu wywołań przy użyciu System.Diagnostics.StackTrace.

+1

robiłem takie rzeczy i złapał mnie z powrotem. Uważaj, może to powodować problemy, jeśli właściwość jest inline: umieszczając [MethodImpl (MethodImplOptions.NoInlining)] zanim program pobierający/ustawiający może pomóc, ale jest niewygodny ... –

6

Chciałbym dowiedzieć się więcej o kontekście, w którym jest to potrzebne, ponieważ wydaje mi się, że powinieneś już wiedzieć, z którą usługą korzystasz w akcesorencie. Jeśli jednak musisz, prawdopodobnie możesz użyć MethodBase.GetCurrentMethod() .Name i usunąć wszystko po get_/set_.

Aktualizacja:

podstawie zmian, chciałbym powiedzieć, że należy użyć dziedziczenia zamiast refleksji. Nie wiem, jakie dane znajdują się w twoim słowniku, ale wydaje mi się, że naprawdę chcesz mieć różne klasy samochodów, np. Sedan, Roadster, Buggy, StationWagon, a nie przechowywać ich w zmiennej lokalnej. Wtedy mielibyśmy implementacje metod, które działają właściwie dla tego typu samochodów. Zamiast dowiedzieć się, jaki rodzaj samochodu posiadasz, a następnie zrobić coś, możesz po prostu wywołać odpowiednią metodę, a obiekt Car robi właściwe rzeczy w zależności od typu.

public interface ICar 
{ 
     void Drive(decimal velocity, Orientation orientation); 
     void Shift(int gear); 
     ... 
} 

public abstract class Car : ICar 
{ 
     public virtual void Drive(decimal velocity, Orientation orientation) 
     { 
      ...some default implementation... 
     } 

     public abstract void Shift(int gear); 

     ... 
} 

public class AutomaticTransmission : Car 
{ 
     public override void Shift(int gear) 
     { 
      ...some specific implementation... 
     } 
} 

public class ManualTransmission : Car 
{ 
     public override void Shift(int gear) 
     { 
      ...some specific implementation... 
     } 
} 
+0

Więcej przykładów na stronie: http://www.codeproject.com /KB/dotnet/MethodName.aspx –

+0

Dodałem trochę więcej objaśnienia mojego kontekstu. –

+0

Twoje aktualizacje pokazują, dlaczego nie powinieneś próbować używać do tego celu refleksji. Korzystając z sugerowanego sposobu myślenia, prawdopodobnie dowiesz się, że jesteś w "GetThisPropertyName" i zdecydujesz, że jesteś ponownie właściwością "ThisPropertyName". Będziesz musiał zmodyfikować przykład, aby wykonać jedno wywołanie stosu "w dół", aby uzyskać faktyczne wywołanie właściwości. Choć jest to możliwe, wygląda na przesadę. Twój kod zostanie posypany kopiami/pastami tych wywołań [GetThisPropertyName()] i nie będzie już bardziej czytelny (IMHO). Przydałby mi się osobiście używanie normalnego dicta dyktującego ciąg. –

3

FWIW I wdrożony system tak:

[CrmAttribute("firstname")] 
    public string FirstName 
    { 
     get { return GetPropValue<string>(MethodBase.GetCurrentMethod().Name); } 
     set { SetPropValue(MethodBase.GetCurrentMethod().Name, value); } 
    } 

    // this is in a base class, skipped that bit for clairty 
    public T GetPropValue<T>(string propName) 
    { 
     propName = propName.Replace("get_", "").Replace("set_", ""); 
     string attributeName = GetCrmAttributeName(propName); 
     return GetAttributeValue<T>(attributeName);    
    } 

    public void SetPropValue(string propName, object value) 
    { 
     propName = propName.Replace("get_", "").Replace("set_", ""); 
     string attributeName = GetCrmAttributeName(propName); 
     SetAttributeValue(attributeName, value); 
    } 

    private static Dictionary<string, string> PropToAttributeMap = new Dictionary<string, string>(); 
    private string GetCrmAttributeName(string propertyName) 
    { 
     // keyName for our propertyName to (static) CrmAttributeName cache 
     string keyName = this.GetType().Name + propertyName; 
     // have we already done this mapping? 
     if (!PropToAttributeMap.ContainsKey(keyName)) 
     { 
      Type t = this.GetType(); 
      PropertyInfo info = t.GetProperty(propertyName); 
      if (info == null) 
      { 
       throw new Exception("Cannot find a propety called " + propertyName); 
      } 

      object[] attrs = info.GetCustomAttributes(false); 
      foreach (object o in attrs) 
      { 
       CrmAttributeAttribute attr = o as CrmAttributeAttribute ; 
       if (attr != null) 
       { 
        // found it. Save the mapping for next time. 
        PropToAttributeMap[keyName] = attr.AttributeName; 
        return attr.AttributeName; 
       } 
       } 
       throw new Exception("Missing MemberOf attribute for " + info.Name + "." + propertyName + ". Could not auto-access value"); 
      } 

      // return the existing mapping 
      string result = PropToAttributeMap[keyName]; 
      return result; 
     } 

Istnieje również zwyczaj atrybut klasy o nazwie CrmAttributeAttribute.

bym zalecamy przeciwko użyciu GetStackFrame() jako część rozwiązania, moja oryginalna wersja rozwiązania był pierwotnie znacznie neater:

return GetPropValue<string>(); 

Ale było 600x wolniej niż wyżej wersji .

3

Nieco mylący przykład, który przedstawiłeś, chyba że po prostu go nie dostaję. Z C# 6.0 można użyć operatora nameof.

public CarType MyProperty 
{ 
    get { return (CarType)this[nameof(MyProperty)]}; 
    set { this[nameof(MyProperty)] = value]}; 
} 

Jeśli masz metodę, która obsługuje swój getter/setter w każdym razie, można użyć atrybutu C# 4.5 CallerMemberName, w tym przypadku nie trzeba nawet powtórzyć nazwę.

public CarType MyProperty 
{ 
    get { return Get<CarType>(); } 
    set { Set(value); } 
} 

public T Get<T>([CallerMemberName]string name = null) 
{ 
    return (T)this[name]; 
} 

public void Set<T>(T value, [CallerMemberName]string name = null) 
{ 
    this[name] = value; 
} 
+0

Ciągle zapominam o magii 'nameof' za każdym razem. znacznie łatwiej jest po prostu wywołać 'nameof (property)' inne niż wszystkie inne rozwiązania jakie widziałem. potwierdzając, że samo wykonanie 'nameof (item.PropertyName) 'działało dla mnie jak urok. chociaż może się różnić dla innych w zależności od ich sytuacji. – Niklas

+1

@Niklas 'nameof' jest jednym z największych ulepszeń składni kiedykolwiek wprowadzonych do C#. Sprawia, że ​​używanie literalnych ciągów znaków w kodzie jest prawie przestarzałe, a więc nie tylko poprawia wygodę, ale także poprawia bezpieczeństwo kodu. Używam go cały czas. –

Powiązane problemy