代码之家  ›  专栏  ›  技术社区  ›  BCS

如何在C中的结构上强制使用工厂#

  •  3
  • BCS  · 技术社区  · 16 年前

    我有一个c结构,在这里我需要禁止对它调用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
    

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

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

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

    我真的需要强制使用一个工厂,这样就可以防止所有的公共构造函数调用。


    完全披露者:为了避免单一依赖,C应用程序将自动转换为D,其中 new Struct() 结果是一个指针,这对我来说是一件很糟糕的事情。然而,这个问题是相关的,所以忽略它。

    5 回复  |  直到 9 年前
        1
  •  11
  •   Community CDub    8 年前

    不能。所有结构都有一个C中定义的公共无参数构造函数。(在CLR中,几乎没有一个是这样的,但它们总是可以像它们那样工作。)见 this question 为什么不能在结构上定义自己的无参数构造器(无论如何在C中)。

    事实上,你 可以 如果您愿意在IL中编写值类型,请阻止此语句。我刚刚检查过,如果您确保您的值类型只有一个无参数的构造函数,并且是内部的,那么您将无法编写 MyStruct ms = new MyStruct(); 但这并不能阻止:

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

    它绕过了新的限制——所以它不会真正为你买任何东西。这真的很好,因为在伊利诺伊州乱搞会很混乱。

    您确定要首先编写结构吗?那是 几乎 从来没有好主意。

        2
  •  3
  •   MrKurt    16 年前

    你不能。

    一个结构中的所有值都必须在构造时初始化,而在构造函数之外是无法初始化的。

    你到底想做什么?结构是值类型,因此大多数操作都会得到一个“新”结构。在结构上强制使用工厂的各种约束是非常困难的。

        3
  •  2
  •   Neil    16 年前

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

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

    对于结构,没有空这样的东西。如果创建一个包含1000个datetime对象的数组,它们都初始化为0,等于datetime.min。运行时的设计者选择了创建它,这样就可以在不调用构造函数n次的情况下创建一个结构数组——这对许多人来说是一个性能冲击,但很多人不会意识到。

    不过,你的工厂理念不错。创建一个接口并公开它,但使该结构成为私有的还是内部的,这是否满足您的需要?这差不多是你能做到的。

        4
  •  0
  •   BenAlabaster    16 年前

    将其放入自己的程序集中,并将不带参数的mystruct()作为内部(VB中的朋友)。使工厂与mystrut()位于同一程序集中,但带有公共访问器。

    现在工厂可以访问no-args mystruct,但是从程序集外部调用的任何内容都必须使用工厂。

    编辑:我的错,我没有考虑到这是一个结构。你不能用一个结构来完成这项工作,只能用一个类来完成——在这种情况下,我前面的语句就是这样。

        5
  •  0
  •   Eljay    9 年前

    您可以创建一个结构来检测它是否处于默认的初始化状态,然后在这种情况下做一些适当的事情。我离开工厂在,但一个建设者也可以是一个适当的工厂在普通,简单的情况下。

    这是很多样板代码。既然你使用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();
            }
        }
    }