我有一个c#struct,我需要禁止在其上调用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#app会被自动转换为D,其中new Struct()
会产生一个指针,这对我来说很糟糕。然而,这个问题是相关的,尽管如此,请忽略它。
答案 0 :(得分:11)
你做不到。根据C#中的定义,所有结构都有一个公共无参数构造函数。 (在CLR中,它们几乎都没有,但是它们总是可以像它们一样。)请参阅this question,了解为什么不能在结构上定义自己的无参数构造函数(无论如何都在C#中)。
事实上,如果您愿意在IL中编写值类型,可以阻止此语句。我刚刚检查过,如果你确定你的值类型只有一个无参数构造函数,并将其设置为内部,那么你将无法编写MyStruct ms = new MyStruct();
但这不会阻止:
MyStruct[] array = new MyStruct[1];
MyStruct ms = array[0];
围绕新的限制 - 所以它并没有真正为你买任何东西。这真的很不错,因为在IL中搞乱会很混乱。
你确定你真的想要首先编写一个结构吗?那几乎从来都不是一个好主意。
答案 1 :(得分:3)
你不能。
结构中的所有值必须在构造时初始化,并且在构造函数之外无法做到这一点。
你究竟想通过这样做完成什么?结构是值类型,因此您将获得大多数操作的“新”结构。要在结构上使用工厂的各种约束将非常困难。
答案 2 :(得分:2)
任何人都可以在不调用构造函数的情况下随时创建结构,只要他们可以访问结构。以这种方式思考:
如果创建一个包含1000个元素的对象数组,它们都会被初始化为null,因此不会调用任何构造函数。
对于结构体,没有null这样的东西。如果创建一个包含1000个DateTime对象的数组,则它们都初始化为零,等于DateTime.Min。运行时的设计者选择创建它,这样你就可以创建一个结构数组,而不需要调用构造函数N次 - 这是许多人都没有意识到的性能损失。
但是你的工厂理念很好。它是否满足您创建界面并公开它的需要,但是使结构变为私有或内部?那就差不多了。
答案 3 :(得分:0)
将它放在自己的程序集中,并将没有args的MyStruct()作为内部(VB中的朋友)。使工厂与MyStruct()具有相同的程序集,但具有公共访问者。
现在工厂可以访问no args MyStruct,但是从程序集外部调用的任何东西都必须使用Factory。
编辑:我的不好,我没有考虑到这是一个结构。你不能用一个结构来做这个,只能用一个类 - 在这种情况下,我之前的陈述就是这样。
答案 4 :(得分: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();
}
}
}