5

我有 ac# 结构,我需要禁止在其上调用 no args 构造函数。

MyStruct a;
/// init a by members   // OK
MyStruct b = MyStruct.Fact(args); // OK, inits by memebers

MyStruct s = new MyStruct(); // can't have that

我这样做主要是为了强制所有成员的显式值,因为没有有效的默认值并且所有成员都必须具有有效值。

在 C++ 中这很容易,添加一个私有构造函数,但 C# 不允许这样做。

有没有办法防止上述情况?

我真的需要强制使用工厂,这样防止所有公共构造函数调用也能正常工作。


完全披露者:为了避免单声道依赖,c# 应用程序被自动翻译为 D ,其中new Struct()产生了一个指针,这对我来说是个麻烦事。然而,尽管如此,这个问题还是相关的,所以请忽略它。

4

5 回答 5

14

你不能。根据 C# 中的定义,所有结构都有一个公共的无参数构造函数。(在 CLR 中几乎没有人这样做,但他们总是可以表现得好像他们有一样。)请参阅这个问题,了解为什么不能在结构上定义自己的无参数构造函数(无论如何在 C# 中)。

事实上,如果您乐于在 IL 中编写值类型,则可以避免这种说法我刚刚检查过,如果您确保您的值类型只有一个无参数构造函数,并将其设为内部,您将无法编写MyStruct ms = new MyStruct();但这并不能阻止:

MyStruct[] array = new MyStruct[1];
MyStruct ms = array[0];

它适用于新的限制 - 所以它并没有真正给你买任何东西。这真的很好,因为在 IL 中乱搞会很乱。

你确定你真的想一开始就写一个结构吗?这几乎从来都不是一个好主意。

于 2008-12-04T20:37:43.220 回答
3

你不能。

结构中的所有值都必须在构造时初始化,并且在构造函数之外没有办法这样做。

你到底想通过这样做来完成什么?结构是值类型,因此您将获得用于大多数操作的“新”结构。在结构上强制执行您将使用工厂的各种约束将非常困难。

于 2008-12-04T20:38:33.483 回答
2

任何人都可以在不调用构造函数的情况下随时创建结构,只要他们有权访问该结构。这样想:

如果您创建一个包含 1000 个元素的对象数组,它们都会被初始化为 null,因此不会调用构造函数。

对于结构,没有 null 之类的东西。如果您创建一个包含 1000 个 DateTime 对象的数组,它们都将初始化为零,即 DateTime.Min。运行时的设计者选择这样做,这样您就可以创建一个结构数组,而无需调用构造函数 N 次——许多人没有意识到存在性能损失。

不过,你的工厂想法是个好主意。它是否满足您创建接口并公开它的需求,但将结构设为私有或内部?这几乎是你将得到的。

于 2008-12-04T20:40:44.960 回答
0

将它放在自己的程序集中,并将没有 args 的 MyStruct() 作为内部(VB 中的朋友)。将 Factory 与 MyStruct() 放在同一个程序集中,但使用 Public 访问器。

现在工厂可以访问无参数的 MyStruct,但是从程序集外部调用的任何东西都必须使用工厂。

编辑:我的错,我没有考虑到这是一个结构。你不能用一个结构来做到这一点,只能用一个类——在这种情况下,我之前的陈述是有效的。

于 2008-12-04T20:37:12.177 回答
0

您可以创建一个结构来检测它是否处于默认初始化状态,然后在这种情况下做一些适当的事情。我离开了工厂,但在常见的简单情况下,构造函数也可能是一个足够的工厂。

这是很多样板代码。既然你使用了 D,你可能会和我一样想,“我希望 C# 有模板混合。”

例子:

using System;

namespace CrazyStruct
{
    public struct MyStruct
    {
        private readonly int _height;
        private readonly bool _init; // Will be 'false' using default(MyStruct).

        /// <summary>
        /// Height in centimeters.
        /// </summary>
        public int Height
        {
            get
            {
                if (!_init)
                {
                    // Alternatively, could have the preferred default value set here.
                    // _height = 200; // cm
                    // _heightInit = true;
                    throw new InvalidOperationException("Height has not been initialized.");
                }
                return _height;
            }
            // No set:  immutable-ish.
        }

        private MyStruct(int height)
        {
            _height = height;
            _init = true;
        }

        public static MyStruct Factory(int height)
        {
            return new MyStruct(height);
        }
    }

    static class Program
    {
        static void Main(string[] args)
        {
            MyStruct my = MyStruct.Factory(195);
            Console.WriteLine("My height is {0} cm.", my.Height);
            try
            {
                var array = new MyStruct[1];
                var ms = array[0];
                Console.WriteLine("My height is not {0} cm.", ms.Height);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Caught the expected exception: {0}.", ex);
            }
            Console.ReadKey();
        }
    }
}
于 2016-01-29T15:32:55.890 回答