6

我正在用 C# 编写一个应用程序,它将进行大量计算。一切都围绕着基本结构——价值。它基本上是双倍的,带有一些额外的参数(精度等)。它必须是一个结构,因为会创建太多的结构来提供堆分配。现在,我需要确保它们都已正确初始化。我不能声明默认的显式构造函数,尽管我提供了默认构造函数,它用 0 初始化所有内容,这在我的域中没有意义。

如果不使用参数调用我的构造函数,就无法拒绝创建实例......?

基本上我需要的是通过这个测试:

[Test]
public void HowDoesThisStructureInitializeByDefault()
{
   Value v = new Value(); - if this did not compile - it would have been ok!

   Assert.AreEqual(0, v.Val); - passes
   Assert.AreEqual(-1, v.Accuracy); - fails
}

如果没有显式调用构造函数并且仍然访问结构,则抛出异常是可以的,但是检查所有时间将花费太多时间。

我现在几乎失去了希望,请帮助!

4

5 回答 5

4

为什么不能定义显式构造函数?这就是他们目的。此外,为什么您认为您“负担不起堆分配”?托管语言中的堆分配非常便宜。你是如何测试这个假设的,即你负担不起堆分配,或者堆分配一开始就更昂贵?

(对于由“一个双参数和几个参数”组成的类型,我怀疑你的大小已经达到堆分配实际上更便宜和更有效的程度)

在任何情况下,如果用户愿意,您都不能阻止用户在值类型上调用默认构造函数。您所能做的就是确保存在一种更好的初始化值的方法,例如非默认构造函数,或者如果您出于某种原因无法创建一个,则可以在调用时创建并初始化您的值类型的函数。

但是,当然,您不能保证人们会真正调用它。

编辑: .NET 中的堆分配基本上只包含一个简单的堆栈推送操作。这是托管(和垃圾收集)语言的好处。运行时本质上使用一个大堆栈作为其堆,因此每次分配只是将堆栈指针增加一点(当然,在检查是否有足够的可用内存之后)。然后垃圾收集器在必要时负责压缩内存。

所以堆分配本身非常便宜。当然,额外的 GC 压力可能会再次减慢您的速度(尽管据我所知,GC 传递所需的时间仅取决于活动对象的数量,而不取决于要 GC 的对象,因此有无数“死”的对象可能不是什么大问题),但另一方面,堆栈分配也不是免费的。值类型是按值传递的,因此每次将类型作为参数传递给函数或返回时,都必须进行复制。我不知道你的值有多大,但一个双精度是 8 个字节,鉴于你有一些额外的参数,我假设 32 个字节。这可能是如此之大,以至于值类型所需的额外复制使其比使用堆分配时要慢。也许。

如您所见,值类型和引用类型都有优势。我不能说在你的情况下哪个更快,但如果我是你,我会非常小心地做出这种假设。如果可能,构建您的代码,以便您可以在引用类型和值类型实现之间切换,并查看哪个效果最好。或者,编写较小的测试来尝试预测每个测试在大规模上的表现。

于 2008-12-06T19:03:44.187 回答
3

您无法摆脱默认构造函数(当然,Jon Skeet 在Why can't I define a default constructor for a .NET in .NET? 中很好地回答了为什么),但您可以创建一个允许您的工厂类使用正确初始化的参数定义结构值。您可以将单元测试与模拟/验证一起使用,以确保当您的代码创建新值时,它们使用工厂。这将是您需要强制执行的约定,因为编译器不会为您强制执行它。

public static class StructFactory
{
    public static Value DefaultValue()
    {
         Value v = new Value();
         v.Value = 0.0;
         v.Accuracy = 15; /* digits */
         return v;
    }
}

...

Value v = StructFactory.DefaultValue();
于 2008-12-06T19:10:42.240 回答
3

结构字段初始化为零(或 null,或通常为 default(T))。

如果您希望 Accuracy 的初始值为 -1,则可以实现 Accuracy 属性,以便当基础字段 == 0 时,该属性返回 -1。

一种可能:

struct Value
{
  int _accuracyPlusOne;

  public int Accuracy
  { 
    get { return _accuracyPlusOne - 1; }
    get { _accuracyPlusOne= value + 1; }
  }
}
于 2008-12-06T19:15:14.430 回答
2

我认为 C# 不允许您在值类型上创建默认构造函数。有几个与您的问题相关的问题:

在 IL 中有一种方法可以做到这一点,但初始化数组仍然不会调用这些构造函数。

于 2008-12-06T19:01:54.503 回答
0

您是否希望使用默认构造函数将精度初始化为 -1?我不认为你可以阻止某人使用new Value(),但你可以添加一个构造函数,让你使用new Value(10)并按照你想要的方式初始化准确性。

请参阅有关 [结构构造函数]( http://msdn.microsoft.com/en-us/library/aa288208(VS.71).aspx)的 MSDN 页面。

如果您遇到 0 的准确度(如果该值永远没有意义!),您总是可以在使用您的结构的代码中抛出异常。

于 2008-12-06T19:00:44.053 回答