2009-07-30 4 views
35

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

Update: У меня есть код, как это:

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

И потому, что мне нужно больше свойств, как это я хотел бы сделать что-то вроде этого:

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

+1 Я тоже задавался вопросом об этом при осуществлении System.Configuration.ApplicationSettingsBase –

ответ

47

Поскольку свойства действительно только методы, которые вы можете сделать это и зачистить get_ вернулся:

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; 
      } 
     } 
    } 

Если профиль производительности вы должны найти MethodBase.GetCurrentMethod() в милях быстрее, чем StackFrame. В .NET 1.1 у вас также будут проблемы с StackFrame в режиме выпуска (из памяти, я думаю, я нашел, что это было в 3 раза быстрее).

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

Я полагаю, что другой вариант, если бы вы были обеспокоены производительностью, заключался в создании фрагмента кода Visual Studio Intellisense, который создает для вас свойство, а также создает строку, соответствующую имени свойства.

+0

Интересно - я не знал о GetCurrentMethod() – BlueMonkMN

+0

Является ли что-то похожее на это для 'Свойства'? –

0

Да, это так!

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

Пробуйте использовать System.Diagnostics.StackTrace, чтобы отразить стек вызовов. Свойство должно быть где-то в стеке вызовов (вероятно, вверху, если вы вызываете его непосредственно из кода свойства).

5

Используйте MethodBase.GetCurrentMethod() вместо этого!

Отражение используется для работы с типами, которые невозможно выполнить во время компиляции. Получение имени аксессуара свойств, в котором вы находитесь, может быть определено во время компиляции, поэтому вы, вероятно, не должны использовать для этого отражение.

Вы все же используете имя метода доступа от стека вызовов, используя System.Diagnostics.StackTrace.

string GetPropertyName() 
    { 
     StackTrace callStackTrace = new StackTrace(); 
     StackFrame propertyFrame = callStackTrace.GetFrame(1); // 1: below GetPropertyName frame 
     string properyAccessorName = propertyFrame.GetMethod().Name; 

     return properyAccessorName.Replace("get_","").Replace("set_",""); 
    } 
+1

Я делал такие вещи, как это и он поймал меня. Опасайтесь, что это может вызвать проблемы, если свойство встроено: поместите [MethodImpl (MethodImplOptions.NoInlining)], прежде чем он сможет помочь, например, getter/setter, но это неудобно ... –

6

Я хотел бы знать больше о контексте, в котором вам это нужно, так как мне кажется, что вы уже должны знать, какое имущество вы работаете в собственности аксессоре. Однако, если вы, вероятно, можете использовать MethodBase.GetCurrentMethod() .Имя и удалить что-либо после get_/set_.

Update:

Исходя из ваших изменений, я бы сказал, что вы должны использовать наследование, а не отражения. Я не знаю, какие данные содержатся в вашем словаре, но мне кажется, что вы действительно хотите иметь разные классы автомобилей, например, Sedan, Roadster, Buggy, StationWagon, не сохраняя тип в локальной переменной. Тогда у вас будут реализации методов, которые делают правильную вещь для этого типа автомобилей. Вместо того, чтобы узнать, какой автомобиль у вас есть, затем что-то делаете, вы просто вызываете соответствующий метод, и объект Car делает правильную вещь, основываясь на том, какой тип он есть.

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

Другие примеры: http://www.codeproject.com /KB/dotnet/MethodName.aspx –

+0

Я добавил еще несколько объяснений моего контекста. –

+0

Ваши обновления просто показывают, почему вы не должны пытаться использовать отражение для него. Используя предложенный способ отражения, вы, вероятно, узнаете, что находитесь внутри «GetThisPropertyName», и решите, что вы используете свойство «ThisPropertyName». Вам нужно будет подстроить пример, чтобы выполнить один вызов стека «вниз», чтобы получить фактический вызов свойства. Хотя это возможно, это похоже на перебор. Ваш код будет посыпать копиями/вставками этого вызова [GetThisPropertyName()], и он не будет более читаемым (IMHO). Лично я придерживаюсь обычного аксессуара string dict. –

3

FWIW я реализовал систему так:

[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; 
     } 

Там также пользовательский атрибут класса называется CrmAttributeAttribute.

я настоятельно рекомендую против использованием GetStackFrame() как часть вашего решения, моя первоначальная версия решения была изначально гораздо аккуратнее:

return GetPropValue<string>(); 

Но это было 600x медленнее, чем выше версия ,

3

Немного запутанный пример, который вы представили, если только я его не получу. С C# 6.0 вы можете использовать оператор nameof.

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

Если у вас есть метод, который обрабатывает ваш геттер/сеттер в любом случае, вы можете использовать C# 4.5 CallerMemberName атрибут, в этом случае вам даже не нужно повторять имя.

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

Я все время забываю о волшебстве 'nameof'. это намного проще просто вызвать 'nameof (свойство)' кроме всех других решений, которые я видел. подтверждая, что просто выполнение 'nameof (item.PropertyName)' работало как прелесть для меня. хотя это может отличаться для других в зависимости от их ситуации. – Niklas

+1

@Niklas 'nameof' - одно из самых больших улучшений синтаксиса, когда-либо введенных в C#. Делает использование буквальных строк в коде почти устаревшим, что не только улучшает удобство, но и повышает безопасность кода. Я использую это все время. –

Смежные вопросы