【问题标题】:Strong Typing a property name in .NET在 .NET 中强键入属性名称
【发布时间】:2010-12-31 08:20:49
【问题描述】:

假设我有一个只有一个属性的类

Public Class MyClass
   Public Property MyItem() as Object
      ....
   End Property
End Class

我必须将属性名称传递给函数调用。 (请不要问为什么要这样做,它是第三方框架)。例如

SomeFunc("MyItem")

但我想做的是,将字符串更改为强类型参数。意思是,如果属性名称被重命名或更改,也应该在这里体现出来。

所以这种类型的东西:

Dim objectForStrongTyping as New MyClass()
SomeFunc(objectForStrongTyping.MyItem().Name())

我确信这不会起作用。有没有办法可以完成这种强类型? (C# 或 VB.NET,任何东西都很酷)

【问题讨论】:

    标签: c# .net vb.net strong-typing


    【解决方案1】:

    您始终可以使用包含 string 常量的静态类,而不是传入 string 文字:

    public static class ObjectForStrongTyping
    {
        public const string MyItem = "MyItem";
        public const string MyOtherItem = "MyOtherItem";
        // ...
    }
    

    您的代码将变为:

    SomeFunc(ObjectForStrongTyping.MyItem);
    

    【讨论】:

    • +1:虽然它不像 LINQ 和 typeof(...) 解决方案那样“自动”,但它超级简单且维护量极少。
    【解决方案2】:

    如果只有一个属性,您可以这样做 - 获取类的第一个属性的属性信息:

    //C# syntax
    typeof(MyClass).GetProperties()[0].Name;
    
    'VB syntax
    GetType(MyClass).GetProperties()(0).Name
    

    EDIT原来,在可以使用表达式的地方,也可以使用projection进行这种反射(C#代码)。

    public static class ObjectExtensions {
        public static string GetVariableName<T>(this T obj) {
            System.Reflection.PropertyInfo[] objGetTypeGetProperties = obj.GetType().GetProperties();
    
            if(objGetTypeGetProperties.Length == 1)
                return objGetTypeGetProperties[0].Name;
            else
                throw new ArgumentException("object must contain one property");
        }
    }
    
    class Program {
        static void Main(string[] args) {
            Console.WriteLine(Console.WriteLine(new { (new MyClass()).MyItem}.GetVariableName()););
        }
    }
    

    有了这个解决方案,类可以有任意数量的属性,你可以得到任何其他的名字。

    【讨论】:

    • 哎哟。现在,我们不必担心属性更改名称,而不必担心添加/重新排序属性?
    • 是的,它很脏,但是根据 OP,该类只有一个属性。请参阅编辑以获得更好的解决方案。
    • 使用编辑,这是一个非常有趣的答案。投影方法很好。有没有推荐哪个更有意义?在 VB.Net 中,匿名类型语法比 lambda 语法更简洁。
    【解决方案3】:

    这是一个使用来自System.Linq.Expressions 的类的解决方案。

    static MemberInfo GetMemberInfo<TObject, TProperty>(
        Expression<Func<TObject, TProperty>> expression
    ) {
        var member = expression.Body as MemberExpression;
        if (member != null) {
            return member.Member;
        }
    
        throw new ArgumentException("expression");
    }
    

    把它扔到某个地方的课堂上(ExpressionHelper?)。

    用法:

    class SomeClass {
        public string SomeProperty { get; set; }
    }
    
    MemberInfo member = GetMemberInfo((SomeClass s) => s.SomeProperty);
    Console.WriteLine(member.Name); // prints "SomeProperty" on the console
    

    【讨论】:

    • 很好,这可以包装到扩展中吗?
    【解决方案4】:

    此解决方案适用于 C# 和 VB.NET,但 VB.NET 的 lambda 函数语法不那么干净,这可能会降低此解决方案在 VB 中的吸引力。我的示例将使用 C#。

    你可以使用 C# 3 的 lambda 函数和表达式树特性来实现你想要的效果。基本上,你会编写一个名为 SomeFuncHelper 的包装函数并这样调用它:

    MyClass objForStrongTyping = new MyClass();
    SomeFuncHelper(() => objForStrongTyping.MyItem);
    

    SomeFuncHelper 的实现如下:

    void SomeFuncHelper(Expression<Func<object>> expression)
    {
        string propertyName = /* get name by examining expression */;
        SomeFunc(propertyName);
    }
    

    lambda 表达式() =&gt; objForStrongTyping.MyItem 被转换为一个表达式对象,该对象被传递给 SomeFuncHelper。 SomeFuncHelper 检查表达式,提取属性名称,然后调用 SomeFunc。在我的快速测试中,假设 SomeFuncHelper 始终如上所示调用(即() =&gt; someObject.SomeProperty),以下代码可用于检索属性名称:

    propertyName = ((MemberExpression) ((UnaryExpression) expression.Body).Operand).Member.Name;
    

    您可能希望阅读表达式树并使用代码使其更健壮,但这是大体思路。

    更新: 这类似于 Jason 的解决方案,但允许辅助函数调用中的 lambda 表达式更简单一些(() =&gt; obj.Property 而不是 (SomeType obj) =&gt; obj.Property)。当然,只有当您已经拥有该类型的实例时,这才会更简单。

    【讨论】:

      【解决方案5】:

      我认为最好的解决方案是使用 T4 生成静态常量(例如T4MVC)。

      public static class StaticSampleClass
      {
          public const string MyProperty = "MyProperty";
      }
      

      相信我,当您有大量调用时,反射和 linq 表达式正在降低应用程序的性能。

      坏事是 T4 在 net core 中消失了。 :(

      C#6.0 中的好东西你可以使用nameof(SampleClass.MyProperty)

      在最坏的情况下,您可以使用以下示例:

      using System.Linq.Expressions;
      
      namespace ConsoleApp1
      {
          public static class Helper
          {
              public static string GetPropertyName<T>(Expression<Func<T, object>> propertyExpression)
              {
                  var member = propertyExpression.Body as MemberExpression;
                  if (member != null)
                      return member.Member.Name;
                  else
                      throw new ArgumentNullException("Property name not found.");
              }
              public static string GetPropertyName<T>(this T obj, Expression<Func<T, object>> propertyExpression)
              {
                  return GetPropertyName(propertyExpression);
              }
          }
      
          public class SampleClass
          {
              public string MyProperty { get; set; }
          }
      
          class Program
          {
              static void Main(string[] args)
              {
                  // Property name of type
                  Console.WriteLine(Helper.GetPropertyName<SampleClass>(x => x.MyProperty));
      
                  // Property name of instance
                  var someObject = new SampleClass();
                  Console.WriteLine(someObject.GetPropertyName(x => x.MyProperty));
      
                  Console.ReadKey();
              }
          }
      }
      

      性能结果(100万次调用):

      StaticSampleClass.MyProperty - 8 毫秒

      nameof(SampleClass.MyProperty) - 8 毫秒

      Helper.GetPropertyName&lt;SampleClass&gt;(x =&gt; x.MyProperty) - 2000 毫秒

      【讨论】:

        【解决方案6】:

        在 C# 6.0 中有一个名为 nameof 的新功能。基本上你可以这样做:

        var name = nameof(MyClass.MyItem);
        

        查看从 C# 到 VB 的 Telerik 代码转换器,这似乎是 VB 的等价物:

        Dim name = nameof([MyClass].MyItem)
        

        因此您可以执行以下操作:

        SomeFunc(nameof(MyClass.MyItem));
        

        这里是微软文档的参考: https://docs.microsoft.com/en-us/dotnet/articles/csharp/language-reference/keywords/nameof

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-02-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-03-12
          相关资源
          最近更新 更多