覆盖结构中的默认值 (c#)
Posted
技术标签:
【中文标题】覆盖结构中的默认值 (c#)【英文标题】:Overriding the Defaults in a struct (c#) 【发布时间】:2010-12-21 17:15:18 【问题描述】:是否可以设置或覆盖结构的默认状态?
例如,我有一个
enum somethinga,b,c,d,e;
以及链接该枚举的 2 个值的结构
struct SomethingData
something type;
int Value;
double Multipler;
SomethingData(something enumVal, int intVal, double DblVal) ...
但是我可以指定默认状态是
SomethingData(something.c,0,1);
【问题讨论】:
您可以将 a 设置为 -2(因此,c=0)。或者甚至可能创建一个默认常量(或者更确切地说,只读静态)。 【参考方案1】:结构构造函数类似于 类构造函数,除了 以下区别:
结构不能包含显式 无参数构造函数。结构 成员被自动初始化 到他们的默认值。一个结构 中不能有初始化程序 形式:基础(参数列表)。
http://msdn.microsoft.com/en-us/library/aa288208(v=vs.71).aspx
所以,简短的回答,不,你不能覆盖默认构造函数(每个结构都有一个无参数的构造函数,你不能隐藏它或覆盖它)...
【讨论】:
如何解决让您指定默认值的方法?我能想到的最好的方法是在结构中创建结构的常量实例,然后将值设置为此而不是调用默认构造函数 @Mike:你可以这样做。但这不会隐藏或消除该类型的默认构造函数。 (请注意,您必须为此使用static readonly
字段而不是const
,因为结构类型的字段不能设为常量。)
虽然很遗憾,尤其是当您声明某个结构的数组,并希望特定结构成员具有特定的默认值时……您必须遍历数组并设置它。 (例如,我从 C 中移植了一个算法,它赋予了 '-1' 特殊的含义)。但是,虽然它的代码更多,但如果我们被允许指定替代默认值,也许我们最终不会做更多的工作?
如何创建一个静态属性Default
来创建您的默认对象?与其调用new Foo()
,不如调用Foo.Default
。这将默认值保存在一个中心位置,并提供最少数量的额外代码。【参考方案2】:
你不能。结构总是有一个默认构造函数,它将每个成员设置为其默认值(null
用于引用类型,0
用于数字类型,false
用于布尔值等)此行为无法更改。
【讨论】:
枚举呢? 编辑,刚刚检查。枚举默认为枚举的第一个索引。这实际上非常强大,尤其是当第一个索引被声明为“无”时。 @Krythic 它们实际上将默认为具有值 0 的任何枚举成员,如果您没有明确分配值,这确实是第一个。但是,如果您执行enum Foo A = 1, B = 0
之类的操作,那么您会发现 default(Foo) == Foo.B
代替。如果您的枚举没有值为 0 的成员,则默认值 根本不会是您的枚举选项之一。【参考方案3】:
您不能覆盖结构的默认(无参数)构造函数。您只能添加带有参数的新构造函数。
http://csharp.2000things.com/2010/10/03/108-defining-a-constructor-for-a-struct/
【讨论】:
【参考方案4】:创建一个类对象将导致所有实例字段在任何东西(甚至类构造函数)可以访问它之前就存在,并且分配一个数组将导致它的所有元素在任何东西可以访问数组之前就存在.这两个操作都会导致分配给这些字段或元素的所有内存清零而不考虑其中存储的数据类型。
当一个类类型的存储位置出现时,它最初会持有一个空引用。当结构类型的存储位置出现时,它的所有字段(以及其中的任何结构字段)将同时进行。与只能通过使用构造函数才能存在的类对象实例不同,结构类型存储位置的存在无需使用任何结构自己的代码。因此,结构的定义对于当“实例”[即struct-type storage locations]应运而生。
从根本上说,结构是用胶带绑定在一起的字段集合。如果一个结构应该表现得像其他东西,它通常应该将其字段设为私有并假装是不可变的[即使结构赋值实际上通过用源中的相应值覆盖其所有字段和结构定义来改变目标结构在这件事上没有发言权]。然而,如果一个结构应该封装一组固定的相关但独立的值(例如,一个点的坐标),可以独立地容纳对其各自类型合法的值的任何组合,一个结构应该简单地公开它的字段。有些人可能会抱怨“可变结构是邪恶的”,但这些邪恶只适用于在结构上调用自变异方法时。将其状态公开为字段的结构的行为类似于用胶带粘在一起的变量集合。如果一个人需要的是一组用胶带粘在一起的变量,那么试图让一个结构假装不可变只会让编程变得更加困难。
【讨论】:
【参考方案5】:有一种解决方法可以通过使用自定义属性 getter 来实现这一点。观察:
public struct Foostruct
private int? _x;
private int? _y;
public int X
get return _x ?? 20; // replace 20 with desired default value
set _x = value;
public int Y
get return _y ?? 10; // replace 10 with desired default value
set _y = value;
这仅适用于值类型(可以用 nullable 包装),但您可以通过将引用类型包装在如下的泛型类中来对引用类型执行类似的操作:
public class Wrapper<TValue>
public TValue Value get; set;
public struct Foostruct
private Wrapper<Tick> _tick;
public Tick Tick
get return _tick == null ? new Tick(20) : _tick.Value;
set _tick = new Wrapper<Tick> Value = value ;
【讨论】:
【参考方案6】:有点相关:我经常想将新的对象初始值设定项语法与不可变值类型一起使用。但是,鉴于典型的不可变值类型实现的性质,无法使用该语法,因为属性是只读的。
我想出了这种方法;在我看来,这仍然满足值类型的不变性,但允许负责实例化值类型的代码更好地控制内部数据的初始化。
struct ImmutableValueType
private int _ID;
private string _Name;
public int ID
get return _ID;
public string Name
get return _Name;
// Infuser struct defined within the ImmutableValueType struct so that it has access to private fields
public struct Infuser
private ImmutableValueType _Item;
// write-only properties provide the complement to the read-only properties of the immutable value type
public int ID
set _Item._ID = value;
public string Name
set _Item._Name = value;
public ImmutableValueType Produce()
return this._Item;
public void Reset(ImmutableValueType item)
this._Item = item;
public void Reset()
this._Item = new ImmutableValueType();
public static implicit operator ImmutableValueType(Infuser infuser)
return infuser.Produce();
class Program
static void Main(string[] args)
// use of object initializer syntax made possible by the Infuser type
var item = new ImmutableValueType.Infuser
ID = 123,
Name = "ABC",
.Produce();
Console.WriteLine("ID=0, Name=1", item.ID, item.Name);
【讨论】:
这不是让 ImmutableValueType 成为可变类型吗? (因为它可以通过 Infuser 类型进行修改)我认为这就是构造函数的用途 编写多个构造函数重载以适应初始化对象的所有可能方式可能会很麻烦/乏味。关于不变性:正如我所提到的,在我看来,不变性是保持不变的。如果不调用注入器的 Produce 方法,您将无法获得对“不可变”对象的引用(好吧,我确实添加了一个隐式转换运算符,因此是否删除它是您的调用)。一旦从注入器的 Produce 方法返回值,注入器就不能再操作该值,因为在这种情况下它是一个值类型。【参考方案7】:每次获取/设置属性时都需要设置默认值调用 InitDefaultValues() 方法
private string _numberDecimalSeparator;
public string NumberDecimalSeparator
get
InitDefaultValues();
return _numberDecimalSeparator;
set
InitDefaultValues();
_numberDecimalSeparator = value;
...
private void InitDefaultValues()
if (!_inited)
_inited = false;
var ci = CultureInfo.CurrentCulture;
_numberDecimalSeparator = ci.With(x => x.NumberFormat).Return(x => x.NumberDecimalSeparator, ".");
...
【讨论】:
结构不应该在属性 getter 中改变自己。如果MyObject.Foo
是一个基础类型是你的结构类型的字段,那么读取MyObject.Foo.NumberDecimalSeparator
将捕获当前的文化;如果它是该类型的属性,则读取MyObject.Foo.NumberDecimalSeparator
将创建底层结构的临时副本,将当前文化捕获到该副本中,计算NumberDecimalSeparator
的返回值,并放弃临时结构。【参考方案8】:
有点笨,但很有效
public readonly static float default_value = 1;
public struct YourStruct
public float yourValue
get
return _yourValue + default_value;
set
_yourValue= value - default_value;
public float _yourValue;
【讨论】:
【参考方案9】:我的解决方案。它也可以。
public struct DisplayOptions
public bool isUpon;
public bool screenFade;
public static DisplayOptions Build()
// Return default value
return new DisplayOptions(true, true);
DisplayOptions(bool isUpon, bool screenFade)
this.isUpon = isUpon;
this.screenFade = screenFade;
public DisplayOptions SetUpon(bool upon)
this.isUpon = upon;
return this;
public DisplayOptions SetScreenFade(bool screenFade)
this.screenFade = screenFade;
return this;
使用默认值
// Use default
UIMaster.Instance.StartScreen("Screen 2", DisplayOptions.Build());
// Use custome
UIMaster.Instance.StartScreen("Screen 2", DisplayOptions.Build().SetScreenFade(false));
UIMaster.Instance.StartScreen("Screen 2", DisplayOptions.Build().SetUpon(false));
【讨论】:
【参考方案10】:这应该可以工作
public struct MyStruct
private string myName;
private int? myNumber;
private bool? myBoolean;
private MyRefType myType;
public string MyName
get return myName ?? "Default name";
set myName= value;
public int MyNumber
get return myNumber ?? 42;
set myNumber = value;
public bool MyBoolean
get return myBoolean ?? true;
set myBoolean = value;
public MyRefType MyType
get return myType ?? new MyRefType();
set myType = value;
//optional
public MyStruct(string myName = "Default name", int myNumber = 42, bool myBoolean = true)
this.myType = new MyRefType();
this.myName = myName;
this.myNumber = myNumber;
this.myBoolean = myBoolean;
[TestClass]
public class MyStructTest
[TestMethod]
public void TestMyStruct()
var myStruct = default(MyStruct);
Assert.AreEqual("Default name", myStruct.MyName);
Assert.AreEqual(42, myStruct.MyNumber);
Assert.AreEqual(true, myStruct.MyBoolean);
Assert.IsNotNull(myStruct.MyType);
【讨论】:
如果你明确地将null
传递给myName
,你最终会得到默认值,这不应该发生。如果将null
分配给MyType
,同样的问题。你也不应该有一个可变的引用类型。他们是邪恶的。最后,拥有属性实际上是与拥有字段的重要语义区别,就像在 OP 中一样,特别是在处理可变引用类型时(这也是它们如此邪恶的原因之一)。
@Servy 如果我删除 MyType
和 MyName
并且只在结构中使用值类型怎么办?那仍然不正确吗?我的目标是拥有一个结构MyConfig
,其中包含一些bool
,默认为true,因此我可以将其用作默认参数,如下所示:public void MyMethod(MyConfig config = default(MyConfig)) //...
谢谢。
它仍然通过将字段更改为属性来更改结构的语义,是的。如果它是不可变的,那可能就不是问题了。【参考方案11】:
这可能有效...
public struct MyStruct
private bool _name;
public string myName
get return (_name ? myName : "Default name");
set _name = true; myName = value;
private bool _num;
public int myNumber
get return (_num ? myNumber : 42);
set _num = true; myNumber = value;
private bool _bool;
public bool myBoolean
get return (_bool ? myBoolean : true);
set _bool = true; myBoolean = value;
private bool _type;
public MyRefType myType
get return _type ? myType : new MyRefType();
set _type = true; myType = value;
没关系 ***Exception
【讨论】:
我认为与每个变量关联的布尔值将初始化为 false,然后在构造函数之外更改值时更改为 true。【参考方案12】:有一个解决方法
public struct MyStruct
public MyStruct(int h = 1, int l = 1)
high = h;
low = l;
public int high;
public int low;
【讨论】:
聪明的主意,但这行不通——你试过了吗?调用new MyStruct()
仍会产生一个对象,其中high
和low
设置为0。
你说得对@cdhowie,我刚刚测试了编译。至少它说这不起作用,所以没有人会尝试哈哈哈。以上是关于覆盖结构中的默认值 (c#)的主要内容,如果未能解决你的问题,请参考以下文章
如何覆盖 Spring Cloud Ribbon 中的ribbon.serverListRefreshInterval 默认值?