Строгий контроль типов имя свойства в.NET

Скажите, что у меня есть класс с одним свойством

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, любая вещь прохладна),

10
задан InfantPro'Aravind' 31 December 2009 в 06:16
поделиться

4 ответа

Вот решение, использующее классы из 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
21
ответ дан 3 December 2019 в 15:06
поделиться

Если бы можно было сделать только одно свойство - получить информацию о первом свойстве класса:

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

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

EDIT Оказывается, где можно использовать выражения, можно также использовать проекцию для такого рода отражения (код на языке 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()););
    }
}

С таким решением класс может иметь любое количество свойств, можно получить любые другие их имена.

1
ответ дан 3 December 2019 в 15:06
поделиться

Вы всегда можете использовать статический класс, который содержит строковые константы, вместо того, чтобы передавать в строку буквально:

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

Ваш код тогда станет:

SomeFunc(ObjectForStrongTyping.MyItem);
0
ответ дан 3 December 2019 в 15:06
поделиться

Это решение работает как на C#, так и на VB.NET, но синтаксис VB.NET для лямбда-функций не так чист, что, вероятно, сделало бы это решение менее привлекательным в VB. Мои примеры будут на C#.

Вы можете достичь желаемого эффекта, используя особенности лямбда-функции и дерева выражений на C# 3. В принципе, вы бы написали функцию-обертку под названием SomeFuncHelper и вызвали ее так:

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

SomeFuncHelper реализуется следующим образом:

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

Выражение лямбда () => objForStrongTyping.MyItem транслируется в объект Expression, который передается в SomeFuncHelper. SomeFuncHelper исследует выражение, вытаскивает имя свойства и вызывает SomeFunc. В моём беглом тесте следующий код работает для извлечения имени свойства, предполагая, что SomeFuncHelper вызывается всегда, как показано выше (т.е. () => someObject.SomeProperty):

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

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

Обновление: Это похоже на решение Джейсона, но позволяет немного упростить выражение лямбда внутри вызова вспомогательной функции (() => obj.Property вместо (SomeType obj) => obj.Property). Конечно, это будет проще, если у вас уже есть экземпляр этого типа.

3
ответ дан 3 December 2019 в 15:06
поделиться