问题:
你需要能够控制结构的初始化,取决于是否想要将结构的所有内部字段初始化为基于字段类型的标准默认值(例如,int 初始化为0,string 初始化为空字符串),或者初始化为一组非标准的默认值,或者初始化为一组预定义的值。
解决方案:
可以使用结构的各种构造函数来实现我们的目标。要将结构的所有内部字段初始化为基于字段类型的标准默认值,只需使用结构的默认初始化即可,稍后会演示这一点。要将结构的字段初始化为一组预定义的值,可以使用重载的构造函数。最后,要将结构初始化为一组非标准的默认值,需要在结构构造函数中使用可选参数。通过可选参数,结果能够将其内部字段设置为构造函数参数列表中可选参数指定的默认值。
例1-8 中的数据结构使用了重载的构造函数初始化结构的所有字段。
例1-8:带有重载构造函数的结构
public struct Data
{
public Data(int intData, float floatData, string strData,
char charData, bool boolData)
{
IntData = intData;
FloatData = floatData;
StrData = strData;
CharData = charData;
BoolData = boolData;
}
public int IntData { get; }
public float FloatData { get; }
public string StrData { get; }
public char CharData { get; }
public bool BoolData { get; }
public override string ToString() => IntData + " :: " + FloatData + " :: " +
StrData + " :: " + CharData + " :: " + BoolData;
}
这是初始化结构字段值的典型方式。同时要注意,存在一个隐式的默认构造函数允许结构将其字段初始化为字段的默认值。不过,你也许想要将每个字段初始化为非默认值。
例1-9 中的数据结构使用重载的、带有可选参数的构造函数,将结构的所有字段初始化
为非默认值。
例1-9:带有包含可选参数的构造函数的结构
public struct Data
{
public Data(int intData, float floatData = 1.1f, string strData = "a",
char charData = 'a', bool boolData = true) : this()
{
IntData = intData;
FloatData = floatData;
StrData = strData;
CharData = charData;
BoolData = boolData;
}
public int IntData { get; }
public float FloatData { get; }
public string StrData { get; }
public char CharData { get; }
public bool BoolData { get; }
public override string ToString() => IntData + " :: " + FloatData + " :: " +
StrData + " :: " + CharData + " :: " + BoolData;
}
当然,可以引入一个新的初始化方法来使得事情更为简单。但是你需要显式调用它,如例1-10 所示。
例1-10:带有显式初始化方法的结构
public struct Data
{
public void Init()
{
IntData = 2;
FloatData = 1.1f;
StrData = "AA";
CharData = 'A';
BoolData = true;
}
public int IntData { get; private set; }
public float FloatData { get; private set; }
public string StrData { get; private set; }
public char CharData { get; private set; }
public bool BoolData { get; private set; }
public override string ToString() => IntData + " :: " + FloatData + " :: " +
StrData + " :: " + CharData + " :: " + BoolData;
}
注意,当使用类似Init 这样的显式初始化方法时,需要为每个属性添加私有的属性setter 来将每个字段初始化。
我们能够以不同的技巧创建例1-8 所示结构的实例。每种技巧都使用了初始化此结构对象的不同方法。第一种技巧使用了default 关键字来创建这个结构,代码如下所示。
Data dat = default(Data);
default 关键字只是创建了这个结构的一个实例,并将它的所有字段初始化为字段的默认值。本质上,所有的数值类型默认为0,bool 类型默认为false,char 默认为'\0',string 和其他引用类型默认为null。
现在,如果你并不介意引用类型和char 设置为null 值,那就太好了;但是假设你需要在创建结构时将这些类型设置为除null 之外的值呢?第二种技巧正是用于解决这个问题的;它使用默认无参构造函数,代码如下所示。
Data dat = new Data();
这一代码使得默认无参构造函数被调用。要附带说明的是,在使用结构的默认无参构造函数时,必须使用new 关键字创建结构的一个实例。如果没有new 关键字,默认构造函数是不会被调用的。因此,下面的代码并没有调用到默认无参构造函数。
Data[] dat = new Data[4];
相反,这会用到该结构每个字段的系统定义默认值。
有两种方法可以解决这个问题。你可以使用冗长的方式创建Data 结构的一个数组,代码为:
Data[] dat = new Data[4];
dat[0] = new Data();
dat[1] = new Data();
dat[2] = new Data();
dat[3] = new Data();
或者
ArrayList dat = new ArrayList();
dat.Add(new Data());
dat.Add(new Data());
dat.Add(new Data());
dat.Add(new Data());
你也可以使用简洁的选择,其中用到了LINQ。
Data[] dataList = new Data[4];
dataList = (from d in dataList
select new Data()).ToArray();
LINQ 表达式迭代整个Data 数组,为每个Data 类型的结构元素显式调用了默认无参构造函数。
如果前两种选择都无法用于你的特定案例,你总是可以创建一个重载的构造函数,为每个想要初始化的字段传入参数。第三种技巧需要使用重载的构造函数来创建此结构的一个新实例,代码如下所示。
public Data(int intData, float floatData, string strData,
char charData, bool boolData)
{
IntData = intData;
FloatData = floatData;
StrData = strData;
CharData = charData;
BoolData = boolData;
}
这个构造函数显式地将每个字段初始化为用户提供的值。
Data dat = new Data(2, 2.2f, "blank", 'a', false);
在C# 6.0 中,你不仅可以选择将结构的字段初始化为系统默认值,或者使用重载的构造函数将字段初始化为用户定义的值,还可以使用带有可选参数的重载构造函数将字段初始化为非系统的默认值,如例1-9 中所示。带有可选参数的构造函数看起来如下所示。
public Data(int intData, float floatData = 1.1f, string strData = "a",
char charData = 'a', bool boolData = true) : this()
{
...
}
使用这种构造函数的一个问题是,你必须至少给这个构造函数提供一个参数值。如果intData 参数也有一个关联的可选参数:
public Data(int intData = 2, float floatData = 1.1f, string strData = "a",
char charData = 'a', bool boolData = true) : this()
{
...
}
那么,下面的代码:
Data dat = new Data();
将调用结构的默认无参构造函数,而不是重载的构造函数。这是必须将至少一个参数传入这个构造函数的原因:
Data dat = new Data(3);
现在我们调用了重载的构造函数,将其***个参数intData 的值设为3,并将其他参数设
置为它们的可选值。
最后一个选择是,你可以将一个显式的初始化方法添加到结构中,用于将字段初始化为非默认值。这一技巧展示在例1-10 中。
将Init 方法添加到结构中后,必须在使用new 或default 关键字初始化结构之后调用它。接着Init 方法将每个字段初始化为非默认值。其他需要做的代码修改仅仅是为结构的属性添加私有的setter 方法。这使得Init 方法可以设置内部字段而无需将它们暴露出来。
参考:
MSDN 文档中的“结构”主题。