【问题标题】:Data passing design pattern of structured data in C#, why not public variablesC#中结构化数据的数据传递设计模式,为什么不是公共变量
【发布时间】:2026-02-10 17:30:01
【问题描述】:

如何“很好地”实现以下模式:(有点 arrrgh 为什么我不能只使用公共变量)

我有一组类可以生成而另一组类可以使用的一大块数据。有多种方法可以生成和使用这些数据。数据集具有固定的层次结构,具有多个级别的值字段。我有数字、枚举、字符串。

因此,在我的应用程序的原型版本中,我以完全公开的方式将一堆类拼凑在一起:

public class AllData
{
    public IdentifyData identifyData;
    public FirmwareData firmwareData;
    public VersionData versionData;

    public TestData testData;
    public ServiceData serviceData;
    public ErrorLog errorLog;

    public AllData()
    {
        identifyData = new IdentifyData();
        versionData = new VersionData();
        firmwareData = new FirmwareData();
        testData = new TestData();
        serviceData = new ServiceData();
        errorLog = new ErrorLog();
    }

}

(...)

public class FirmwareData
{
    public DataStatus status;

    public int build;
    public int variant;

    public FirmwareData()
    {
        status = new DataStatus();
    }
}      

(Etc.)

现在我正在做软件的真实版本,并且想要做正确的事情,所以我开始对所有东西进行财产化。但是,我觉得这样做会影响代码。它变得又大又丑,在我的情况下没有出现好处。

我考虑过使用结构,但我真的不想一直按值传递所有这些数据。此外,我需要在这里和那里进行一些范围和完整性检查。

关于如何以一种不错的方式实现此数据模型的任何线索? (请注意,我在 OOP 和 C# 方面不是很有经验)

如果可能的话,我希望数据在移交给消费者类时变为只读,如何做到这一点?

感谢和问候

拉尔斯

【问题讨论】:

  • er.... 只需在末尾添加 {get;set;} 即可创建属性 - 没那么难看;或{get;private set;},如果您不希望外部代码设置它...?也许{get;internal set;} 如果您不希望不同程序集中的代码能够使用设置器...?
  • 是的,还不错。但是我可以用 .NET 框架 2.0 做到这一点吗? (使用 Visual Studio 2010)
  • 是的;这是一种语言特性,而不是运行时特性——C# 4 编译器对此很好,即使针对 .NET 2.0

标签: c# oop class design-patterns struct


【解决方案1】:

无论你做什么,都不要使用structs。 特别是如果它们是可变的!

只需使用属性 - 它们通常比字段更可取(特别是如果您打算使用使用 TypeDescriptor 来获取组件信息的前端系统 - Asp.Net MVC 会这样做)。

我在这里的起始规则是,如果它是公共的或受保护的(在大多数情况下),我将使用一个属性。如果它是受保护的或私有的并且不可变,那么我会考虑使用只读字段。不过,这条规则总有例外,这只是我自己的起点。我将很少在单元测试的帮助类中使用公共字段。

只读问题是另一回事,实际上只有确实使用属性才能解决。

我会使用只读接口:

public interface IMyData
{
   int Value { get; }
}

如果你需要,你的类可以被读/写:

public class MyData : IMyData
{
  public int Value { get; set; }
}

实现了接口——使用并理解MyData的代码(即你的生产者)可以写入它,但只需要读取的代码只使用接口。使用这样的抽象还具有允许您的消费者代码以任何形式使用您的数据的额外好处。例如。如果这是您选择的 ORM,您可以将接口实现添加到由 EF 生成的实体类型。

但请注意,您不能总是全面实施只读 - 特别是如果您打算使用通用字典等类型。

对于在构造后确实必须始终不可变的对象,请使用构造函数并隐藏 setter。所以再次上MyData 类:

 public int Value { get; private set; }     

 public MyData(int value){
   Value = value;
 }

当然,您也可以为此使用只读字段(但这两种方法实际上都不会阻止真正想要修改您的对象的人能够这样做,因为这两种方法都可以通过反射来颠覆)。

【讨论】:

  • 感谢您的启发性回答。只读界面是一个好主意,我可以尝试。但它仍然会使我当前的功能齐全的代码膨胀成三倍或四倍大小;)(在做事时总是让我烦恼,推荐的方式意味着代码行数的巨大增加)
  • 好吧 - 问问你自己,你这样做是因为你想要,还是因为实际上你需要为了达到你的目标?如果它是其中的第二个,那么您正在谈论的“臃肿”(我不得不说我不同意,这里有几个接口并且没有问题)实际上是必要的代码。或者 - 换一种说法 - 你不能只用两个*制造汽车! :)
【解决方案2】:

设计模式在这里没有用,这与您希望如何组织类及其关系有关。

首先将所有字段声明为private,并在类方法中公开以对数据“做事”,或者在某些情况下返回它们,但尽量避免客户端可以直接访问字段并可能更改他们。

http://en.wikipedia.org/wiki/Encapsulation_(object-oriented_programming)

这只是一个起点。

【讨论】:

  • 嗯,最初我考虑让数据承载类执行数据解析任务,并且只公开结果只读。但是我们有不同的方式来获取这些数据,涉及不同的处理,所以我真的只希望提到的类是数据载体。
【解决方案3】:

如果您的数据布局将一直固定并且不需要任何数组,那么暴露字段结构可能是完美的选择。字段应该封装在属性中的概念在应用于类时是好的,但在应用于结构时通常是不好的。从根本上说,结构是用胶带绑定在一起的字段的集合。如果struct1struct2 是相同的结构类型,则struct1 = struct2 语句将通过用struct2 中相应字段的值覆盖其所有字段来改变struct1。结构类型类型的定义中没有任何东西可以改变它。鉴于结构的所有字段总是通过批量赋值公开暴露于突变,因此最好有一个结构将自己宣传为用胶带粘在一起的一堆变量,而不是有一个假装是其他东西的结构。

具有讽刺意味的是,使用暴露字段结构的最大问题是它们最大的资产:许多使用暴露字段结构(尤其是嵌套结构)的便捷方式在使用其他数据类型时没有方便的等价物。如果有一个嵌套结构类型的数组并希望更新内部元素,可以执行以下操作:

三角列表[索引].Vertex1.X += 4;

并更新部分元素。如果将数组替换为List<TriangleStruct>,则代码必须变为:

var temp = TriangleList[index]; temp.Vertex1.X += 4; 三角列表[索引] = temp;

还不算太糟糕。但是,假设需要更改类型以支持任意多边形。不能再用暴露字段结构表示类型(如果它包含一个数组,则复制该结构不会复制该数组而只是一个引用;更改一个结构副本中的坐标会因此同时更改两者,可能导致讨厌的错误)。任何操纵结构的代码都必须完全重写。

在决定是否使用公开字段结构或其他数据类型时,请考虑能够使用方便的访问形式在多大程度上会有所帮助,以及结构可能发生变化的可能性。如果事物的使用方式意味着如果不重写几乎所有使用它的代码就无法改变它的语义,并且将它作为结构访问会更方便,那么使用暴露的字段结构。没有理由让你的代码变得更繁琐,以适应未来如果 YAGNI(你不需要它)永远不会发生的变化。

【讨论】: