10

假设我有一堂课有一个属性

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,任何东西都很酷)

4

6 回答 6

21

这是使用来自 的类的解决方案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
于 2009-12-31T06:18:44.747 回答
5

在 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

于 2017-05-20T14:08:42.827 回答
3

此解决方案适用于 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 表达式() => objForStrongTyping.MyItem被转换为一个 Expression 对象,该对象被传递给 SomeFuncHelper。SomeFuncHelper 检查表达式,提取属性名称,然后调用 SomeFunc。在我的快速测试中,以下代码可用于检索属性名称,假设 SomeFuncHelper 始终如上所示调用(即() => someObject.SomeProperty):

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

您可能想要阅读表达式树并使用代码以使其更加健壮,但这是一般的想法。

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

于 2009-12-31T06:41:00.507 回答
1

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

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

'VB syntax
GetType(MyClass).GetProperties()(0).Name

编辑原来,你可以在哪里使用表达式,你也可以使用投影来进行这种反射(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()););
    }
}

使用此解决方案,该类可以具有任意数量的属性,您将能够获得任何其他名称。

于 2009-12-31T06:18:23.560 回答
0

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

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

您的代码将变为:

SomeFunc(ObjectForStrongTyping.MyItem);
于 2009-12-31T06:13:21.143 回答
0

我认为最好的解决方案是使用 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<SampleClass>(x => x.MyProperty)- 2000 毫秒

于 2017-05-20T12:01:44.243 回答