Подтвердить что ты не робот

Есть ли шаблон С# для строго типизированных членов класса с внешними методами set/get?

Я имею следующую структуру и хотел бы получить решение с двумя преимуществами следующих двух классов. Первый класс использует строки и строго типизированные элементы:

public class UserSessionData
{
    private string Get(string key)
    {
        throw new NotImplementedException("TODO: Get from external source");
    }
    private void Set(string key, string value)
    {
        throw new NotImplementedException("TODO: Set in external source");
    }

    public string CustomerNumber {
        get { return Get("CustomerNumber"); }
        set { Set("CustomerNumber", value); }
    }
    public string FirstName {
        get { return Get("FirstName"); }
        set { Set("FirstName", value); }
    }
    public string LastName {
        get { return Get("LastName"); }
        set { Set("LastName", value); }
    }

    // ... a couple of hundreds of these
}

Я могу представить альтернативу, являющуюся методом Get и Set с параметром enum. Вот второй класс:

public class UserSessionData
{
    public enum What {
        CustomerNumber, FirstName, LastName, // ...
    }

    public string Get (What what) { return MyExternalSource(what); }
    public string Set (What what, string value) { return MyExternalSource(what); }
}

Но потребительская сторона класса № 2 не очень хороша:
UserSessionData.Get(UserSessionData.What.CustomerNumber)
Сравните его с первым классом: UserSessionData.CustomerNumber

Есть ли типичный способ вызова методов Get и Set в моем примере первого класса? С другой стороны: Как получить преимущества обоих классов, т.е. Поддерживаемость строго типизированных членов и симпатичный синтаксис?

4b9b3361

Ответ 1

.Net 4.5 или новее

Если вы используете .Net 4.5 или новее, вы можете использовать CallerMemberNameAttribute, чтобы вы могли называть его следующим образом:

public string CustomerNumber {
    get { return Get(); }
}

Чтобы сделать эту работу, измените метод Get, добавив атрибут к параметру:

private string Get([CallerMemberName] string key)
{
    ...
}

Уведомление о производительности: Компилятор вставляет строку как параметр на вызывающий сайт, поэтому это быстро.


.Net 4.0 или ранее

Если вы используете .Net 4.0 или более ранние версии, вы можете использовать строго типизированные имена свойств вместо ввода строк вручную, но вам необходимо реализовать метод как этот для извлеките имя свойства из Expression, а затем вы можете вызвать его с помощью выражения:

public string CustomerNumber {
    get { return Get(() => this.CustomerNumber ); }
}

Установки могут быть реализованы таким же образом.

Уведомление о производительности: Строка извлекается во время выполнения, поэтому это медленнее, чем использование CallerMemberNameAttribute.

Ответ 2

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

Добавить новый элемент → Текстовый шаблон

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".cs" #>
<# var properties = new string [] {
      "CustomerNumber", // This is where you define the properties
      "FirstName",
      "LastName"
}; #>
class UserSessionData {
<#  
  foreach (string propertyName in properties) 
  { #>
  public string <#= propertyName #>{
    get { return Get("<#= propertyName #>"); }
    set { Set("<#= propertyName #>", value); }
  }
<# } #>
}

Дополнительная информация @Создание кода времени разработки с использованием текстовых шаблонов T4

Ответ 3

Как насчет перечисления с некоторыми методами расширения, например:

// usage:
[TestMethod]
public void example()
{
    UserSessionData.CustomerNumber.Set("cust num");
    Assert.AreEqual("cust num", UserSessionData.CustomerNumber.Get());
}

// implementation:
public enum UserSessionData
{
    CustomerNumber,
    FirstName,
}

public static class UserSessionDataHelper
{
    private static Dictionary<string, string> values = new Dictionary<string, string>();

    private static string GetName(this UserSessionData field)
    {
        return Enum.GetName(typeof(UserSessionData), field);
    }

    public static string Get(this UserSessionData field)
    {
        return values[field.GetName()];
    }

    public static void Set(this UserSessionData field, string value)
    {
        values[field.GetName()] = value;
    }
}

Ответ 4

Этот шаблон позволяет свободно связать ключи ресурсов. Хешмап, за исключением любого типа ключа, с целью расширения. Вы часто видите это с системными сообщениями в контейнерах IoC.

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

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

Ответ 5

Если вы используете .NET 4.0 или новее, вы можете использовать DynamicObject и переопределить его TryGetMember и TrySetMember, чтобы сделать это полностью динамичным. Однако это не будет строго напечатано.

Ответ 6

Я не знаю, почему такая простая душа еще не была предложена:

public class UserSessionData
{
    private string Get(What what)
    {
        throw new NotImplementedException("TODO: Get from external source");
    }
    private void Set(What what, string value)
    {
        throw new NotImplementedException("TODO: Set in external source");
    }

    public string CustomerNumber {
        get { return Get(What.CustomerNumber); }
        set { Set(What.CustomerNumber, value); }
    }

    // ... 
}

public enum What
{
    CustomerNumber, FirstName, LastName, // ...
}

И использование, которое вам так понравилось: userSessionData.CustomerNumber

Если ваш "внешний источник" предпочитает строки, вы можете сделать What enum private и cast enum для строки.