对于具有外部set / get方法的强类型类成员,是否有C#模式?

时间:2013-07-09 14:26:12

标签: c# .net c#-4.0

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

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
}

我可以想象一种替代方案是GetSet方法,其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方法?换句话说:如何从两个类中获得好处,即强类型成员的可维护性和漂亮的语法?

6 个答案:

答案 0 :(得分: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或更早版本,您仍然可以使用强类型属性名称而不是手动键入字符串,但是您需要实现方法like this以从Expression中提取属性名称,然后你可以使用表达式调用它:

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

可以以相同的方式实现setter。

性能提示:字符串是在运行时提取的,因此比使用CallerMemberNameAttribute要慢。

答案 1 :(得分: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); }
  }
<# } #>
}

更多信息@ Design-Time Code Generation by using T4 Text Templates

答案 2 :(得分: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;
    }
}

答案 3 :(得分:0)

此模式允许松散耦合的资源键。出于可扩展性的目的,除了任何键类型之外的散列映射。您经常在IoC容器中看到系统消息。

根据我的经验,这种模式可以为所需文档的成本提供极大的灵活性。考虑使用约定而不是实现来解决问题。我尽可能坚持枚举值。

  • 它们是具有强名的不可变原语。
  • 可以将它们分组为父类型。
  • 如果需要,可以非常简单地迭代它们。
  • 他们回应重构。

答案 4 :(得分:0)

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

答案 5 :(得分: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枚举为私有并将枚举转换为字符串。