20

我有以下结构,并且想要一个具有以下两个类的好处的解决方案。第一类是使用字符串和强类型成员:

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
}

我可以想象另一种方法是带有参数的Getand方法。这是第二课:Setenum

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 方法的强类型方法?换一种说法:我如何从这两个类中获得好处,即强类型成员的可维护性和美观的语法?

4

6 回答 6

34

.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 慢。

于 2013-07-09T14:36:40.150 回答
8

您可以使用 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 文本模板生成设计时代码

于 2013-07-09T14:41:41.830 回答
1

带有一些扩展方法的枚举怎么样,如下所示:

// 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;
    }
}
于 2013-07-09T15:15:22.380 回答
0

这种模式允许松散耦合的资源键。出于可扩展性的目的,除任何键类型外的哈希图。您经常会在 IoC 容器中的系统消息中看到这一点。

根据我的经验,这种模式在所需文档的成本方面具有很大的灵活性。考虑用约定而不是实现来解决问题。我尽量坚持枚举值。

  • 它们是具有强名称的不可变原语。
  • 它们可以被分组到它们的父类型中。
  • 如果需要,它们可以非常简单地迭代。
  • 他们响应重构。
于 2013-07-09T14:34:28.747 回答
0

如果您使用的是 .NET 4.0 或更高版本,则可以使用DynamicObject并覆盖其TryGetMemberTrySetMember方法以完全动态地执行此操作。不过,这不会是强类型的。

于 2013-07-09T14:49:09.440 回答
0

我不知道为什么还没有提出这样一个简单的灵魂:

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将枚举设为私有并将枚举转换为字符串。

于 2014-01-29T11:43:55.973 回答