使用一个结构而不是给构造函数8个参数是正确的吗



我只是在想,

如果我需要为CCD_ 1类编写构造函数,公寓类具有ApartmentSizeNumberOfBedroomsNumberOfShowersCurrentMarketValuePurchasePrice等属性。

创建一个名为ApartmentProperties的结构,并将上述所有属性作为该结构的成员,这样当我需要构造一个单元时,构造函数所需要的只是该结构,这会更可读吗?

如果没有,还有什么更优雅的方式呢?

看起来有点循环。您有一个对象,其构造函数需要所有这些值,所以您正在创建一个包含这些值的对象。现在你有了一个需要所有这些值的结构。。。如何创建结构?

你的提议是:

ApartmentProperties props = new ApartmentProperties(size, numBedrooms, numShowers, marketValue, purchasePrice);
Apartment apt = new Apartment(props);

这样做似乎没什么不同:

Apartment apt = new Apartment(size, numBedrooms, numShowers, marketValue, purchasePrice);

如果这是您唯一一次使用ApartmentProperties结构,而您所做的只是将所有这些值放入其中,将其传递给构造函数,然后再次将它们取出,那么这就没有必要了。这只是一个需要思考的问题。

在评论中,有人建议,如果你不喜欢构造函数上有这么多参数,那么你可以这样做:

Apartment apt = new Apartment();
apt.Size = size;
apt.NumBedrooms = numBedrooms;
apt.Numshowers = numShowers;
apt.MarketValue = marketValue;
apt.PurchasePrice = purchasePrice;

我不太喜欢这种方法。是的,现在你的构造函数更小、更整洁了,但你所做的就是把它里面整齐地封装的逻辑移到你调用它的地方。这使得调用代码负责确保所有属性都得到了正确分配。我建议把赋值留在构造函数中——它只是让你假设Apartment对象已经分配了它的属性,因为你知道构造函数会完成它

当然,也有一个中间立场。您可以去掉的一些构造函数参数,只留下必须存在的那些参数。例如,的公寓总是有一个尺寸、多间卧室和多个淋浴间,但你可能不知道它的当前价值或购买价格——也许有人在那里住了一段时间,最近还没有估价。在这种情况下,您可以将构造函数减少到只有三个参数,并允许调用代码分配其他(如果已知)。或者更好的是,用不同数量的参数重载构造函数。只要确保你有某种方法来确定是否已经分配了一个值(例如,可能有一些奇怪的情况下,零的销售价格是有效的,但也许你知道-1永远不会有效)。如果您需要非常确定,请使用Apartment0,如果尚未分配,则返回null。

首先,我建议您根本不要使用struct。应使用class

价值类型使用指南指出

建议您为满足以下任何条件的类型使用结构:

  1. 表现得像原始类型
  2. 实例大小小于16字节
  3. 是不可变的
  4. 值语义是可取的

出于性能原因,8个字段对于结构来说太大了。有关更多信息,请参阅此。

您可以使用无参数(默认)构造函数,并依赖类型初始化器synctax来初始化属性:

public class Apartment
{
    public Apartment()
    {
        // set default values
    }
    // define properties
    public int NumberOfBedRooms { get; set; }
    // ...
}
var apt = new Apartment {
  ApartmentSize = 100,
  NumberOfBedrooms = 3,
  NumberOfShowers = 2,
  CurrentMarketValue = 100000m,
  PurchasePrice = 100000m
}

这样就不必编写构造函数重载,类的用户可以决定初始化什么。

结构的核心是一堆用胶带粘在一起的变量。具有私有字段的结构可能会试图像其他结构一样工作,但具有公开公共字段的结构会像一堆用胶带粘在一起的变量一样工作。在想要用胶带把一堆变量粘在一起的情况下,暴露的字段结构通常是完美的选择。

请注意,微软的指导方针假设代码希望一切都像Object;它们也是在.NET支持泛型类型之前编写的,当时通常情况下,如果不首先将它们转换为Object,就无法将它们存储在集合中。在需要语义与Object(*)的导数一致的东西的情况下,通常遵循指南是好的。然而,如果一个人想要的只是一个存储位置(字段、变量等),用胶带将一堆变量粘在一起,那么就不应该遵循它们。事实上,许多准则在应用于特定场景时都是落后的。

(*)结构类型的存储位置不包含Object的导数,也不包含对其的引用;相反,它保存结构的公共字段和私有字段的连接内容。如果一个结构类型被强制转换为引用类型,系统将创建一个新的堆对象,其中的字段与该结构的字段相匹配,并将结构的字段复制到新对象的字段中;生成的对象将在很大程度上表现为类对象,其字段和方法与结构的字段和方法相匹配。如果一个结构不遵循MS准则,则存储位置类型和堆类型的行为会有所不同;MS认为这总是一件坏事,但如果一个人想要一个存储位置来容纳一堆用胶带粘在一起的变量,那么拥有一个行为干净的结构(类不能做的事情)比假装是一个类对象来最好地(相当笨拙)模拟这样的事情更有用。

将结构作为值参数传递的成本与单独传递结构的所有公共和私有字段的成本大致相同。将结构作为引用参数(refout)传递具有固定成本,与字段数无关。在结构类型的可变存储位置上调用实例方法相当于在其参数列表的开头调用具有额外参数ref StructType this的方法;如果试图在临时或不可变的位置上调用someStruct.Foo(),编译器将静默地替换var temp = someStruct; temp.Foo();;因为(仍然!)没有办法告诉编译器这种替换不能安全地应用于特定方法,所以通常应该避免编写这种替换不安全的方法。

对于您的特定场景,我认为一个简单的公开字段结构可能非常完美。它允许按任何方便的顺序设置参数值,使构造具有相似但不相同参数的多个对象变得容易,等等。从性能的角度来看,将结构作为ref参数有时可能比传递值快一点,但是,除非同一个结构被多次传递,否则我希望按值传递应该很好(如果您使用公开的字段结构,我希望JITter可能会生成大致相当于将每个结构字段作为单独的参数传递的代码)。

我建议您这样做:

你应该创建一个这样的类:

public class MyClass
{
    ...
    public int ApartmentSize{get;set;}
    ...
}

在Apartment类的构造函数中,执行以下操作:

Apartment(MyClass obj)
{
  // access the values like: obj.ApartmentSize  
}

Struct对此来说是过度杀伤