说我有一个有一个属性的类
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,任何事情都很酷)
答案 0 :(得分: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
答案 1 :(得分:5)
在C#6.0中有一个名为nameof的新功能。基本上你可以这样做:
var name = nameof(MyClass.MyItem);
查看从C#到VB的Telerik代码转换器,它似乎是VB的等价物:
Dim name = nameof([MyClass].MyItem)
所以你可以做到以下几点:
SomeFunc(nameof(MyClass.MyItem));
以下是对microsoft文档的引用: https://docs.microsoft.com/en-us/dotnet/articles/csharp/language-reference/keywords/nameof
答案 2 :(得分:3)
这个解决方案适用于C#和VB.NET,但lambda函数的VB.NET语法并不干净,这可能会使这个解决方案在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检查Expression,提取属性名称,并调用SomeFunc。在我的快速测试中,以下代码用于检索属性名称,假设SomeFuncHelper总是如上所示被调用(即() => someObject.SomeProperty
):
propertyName = ((MemberExpression) ((UnaryExpression) expression.Body).Operand).Member.Name;
您可能希望阅读表达式树并使用代码使其更加健壮,但这是一般的想法。
更新:这类似于Jason的解决方案,但允许helper-function调用中的lambda表达式更简单(() => obj.Property
而不是(SomeType obj) => obj.Property
)。当然,如果你已经有一个类型的实例,这只会更简单。
答案 3 :(得分:1)
如果只有一个属性,你可以这样做 - 获取该类的第一个属性的属性信息:
//C# syntax
typeof(MyClass).GetProperties()[0].Name;
'VB syntax
GetType(MyClass).GetProperties()(0).Name
编辑事实证明,在可以使用表达式的地方,您还可以使用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()););
}
}
使用此解决方案,该类可以包含任意数量的属性,您可以获得其他任何属性。
答案 4 :(得分:0)
您总是可以使用包含string
常量的静态类,而不是传入string
字面值:
public static class ObjectForStrongTyping
{
public const string MyItem = "MyItem";
public const string MyOtherItem = "MyOtherItem";
// ...
}
您的代码将成为:
SomeFunc(ObjectForStrongTyping.MyItem);
答案 5 :(得分:0)
我认为最好的解决方案是使用T4生成静态常量(例如T4MVC)。
public static class StaticSampleClass
{
public const string MyProperty = "MyProperty";
}
当你有很多调用反射时,请相信我,而linq表达式会降低你的应用程序的性能。
糟糕的是T4在网核中消失了。 :(
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 ms