【问题标题】:Is there a C# pattern for strongly typed class members with external set/get methods?具有外部 set/get 方法的强类型类成员是否有 C# 模式?
【发布时间】:2013-07-07 05:07:00
【问题描述】:

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

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
}

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

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

【问题讨论】:

  • Get(CustomerNumber.GetType().Name);:这不等同于Get("CustomerNumber");。相反,它相当于Get("String");
  • 在静态类中使用公共非静态属性有什么意义?
  • 您可以使用设计时T4 Templates 生成成员属性。
  • @Tigran:没有意义。它根本无法编译。
  • @DanielHilgarth:除了编译,我很难理解需求。

标签: c# .net c#-4.0


【解决方案1】:

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

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 和类中的属性,并确保它们匹配。
【解决方案2】:

.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 thisExpression 中提取属性名称,然后您可以使用表达式调用它:

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

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

性能说明:字符串是在运行时提取的,所以这比使用CallerMemberNameAttribute要慢。

【讨论】:

  • +1。我将此方法用于 MVVM 应用程序中的所有 OnNotifyPropertyChanged 调用。由于预编译的表达式树,它表现良好。
  • 或者,每个属性主体都可以包含这个固定代码:get { return Get(System.Reflection.MethodBase.GetCurrentMethod().Name.Substring(4)); } set { Set(System.Reflection.MethodBase.GetCurrentMethod().Name.Substring(4), value); }
  • +1,不知道CallerMemberNameAttribute,这真的很酷!
  • GetCurrentMethod 不是比仅仅传递一个字符串慢很多吗? IIRC 大多数 ORM 框架切换到字符串/表达式方式。
  • @Gusdor 表达式树没有预编译,它们每次都被构建。比较贵。即使您摆脱了外部依赖项(例如 lightbricko 示例中的 this)。
【解决方案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;
    }
}

【讨论】:

  • 感谢您的回答,向您的努力致敬!在终点线上,我必须接受@lightbricko 的回答,因为它的语法将调用缩短了".Get()".Count 个字符...
【解决方案4】:

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

【讨论】:

  • 不幸的是,这不是类型安全的。然而,这也是我的第一直觉。
  • 确实如此,但从目前给出的答案中可以清楚地看出,Simeon 将不得不忍受他理想情况下的某种缺陷,无论是实际调用还是类型转换更麻烦。
  • 感谢您的努力和建议!在旁注中,我在接受的答案中找不到任何缺点(这当然很棒!)
  • 就我个人而言,我会不惜一切代价避免使用几百个纯样板属性定义(缺点),除非有非常具体的原因(例如,该类需要满足关于反射的某些期望)。似乎可以使用另一种方法更优雅地解决手头的问题。
【解决方案5】:

您可以使用 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

【讨论】:

  • 问题是没有生成代码墙。问题是维护它。维护在代码中作为文字字符串提供的属性名称很困难。
  • @GSerg 它们只列出一次,在本例中的模板中。如果您需要更改类,您只需编辑模板。另外,就像我说的,如果必须,您可以使用枚举。
  • @GSerg,对不起,我没有关注。您必须在某处至少写一次,在这种情况下,它们被写在一个易于维护的列表中。就个人而言,这是我(并且确实)的方式,所以+1
  • @Blindy 一个场景:您使用模板生成这个类,然后下周公司的另一个开发人员感觉要重构并到处更改一些属性名称,包括您的一些类。 IDE 会正确更新整个代码库中更改的属性名称,但属性中的文字字符串不会更改。你的代码现在是错误的。
  • 是的,但是错误的方式无法编译。这不是运行时绑定,它仍在正确编译,同时具有完整的 Intellisense 支持。真的不能要求更多(如果您正在为 4.5 编写代码,那么除了那个神奇的属性)。
【解决方案6】:

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

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

  • 它们是具有强名称的不可变原语。
  • 可以将它们分组到它们的父类型中。
  • 如果需要,可以非常简单地对其进行迭代。
  • 他们响应重构。

【讨论】:

  • 嗨,我不明白这是如何回答我的问题的。您将哪种模式称为“这种模式”?您是否建议我使用问题中包含的enum 变体?一些代码将有助于解释您在这里提倡的内容
  • 我提倡的是约定优于代码。如果您可以提供帮助,请不要使用强类型系统使您的代码库复杂化。接受的答案非常好。我建议更好地在问题中定义“更清洁”...
  • 好的,那是在我的问题中使用第二类吗?我已经有了那个解决方案,但是语法很混乱。我进一步改进了我的问题,定义了“更清洁”并扩展了关键问题
猜你喜欢
  • 1970-01-01
  • 2011-02-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多