简体   繁体   English

您可以在 c# 中的只读结构上设置默认参数吗?

[英]Can you have default parameters on a readonly struct in c#?

It appears that default parameters do not work on a readonly struct in c#.似乎默认参数不适用于 c# 中的只读结构。 Am I misunderstanding something?我是不是误会了什么?

   public readonly struct ReadonlyStruct
   {
      public ReadonlyStruct(int p1 = 1, byte p2 = 2, bool p3 = true)
      {
         P1 = p1;
         P2 = p2;
         P3 = p3;
      }

      public int P1 { get; }
      public byte P2 { get; }
      public bool P3 { get; }
   }

Instantiating an instance of this struct using var test = new ReadonlyStruct();使用var test = new ReadonlyStruct(); does not appear to honor the default values.似乎不遵守默认值。 What am I doing wrong?我究竟做错了什么?

Counter-intuitively you can't have all default parameters or an explicit parameterless constructor on a struct , this is not limited to readonly struct .与直觉相反,您不能在struct上拥有所有默认参数或显式无参数构造函数,这不仅限于readonly struct

Unlike a class, a struct is value-type and is not required to have a constructor , so in your case you aren't providing any parameters at all, hence the constructor never gets called.与 class 不同, struct值类型,不需要有构造函数,因此在您的情况下,您根本不提供任何参数,因此构造函数永远不会被调用。

As noted in the documentation如文档中所述

Limitations with the design of a structure type结构类型设计的限制

When you design a structure type, you have the same capabilities as with a class type, with the following exceptions:设计结构类型时,您具有与 class 类型相同的功能,但以下情况除外:

You can't declare a parameterless constructor.您不能声明无参数构造函数。 Every structure type already provides an implicit parameterless constructor that produces the default value of the type.每个结构类型都已经提供了一个隐式的无参数构造函数,该构造函数产生该类型的默认值。

When you don't supply parameters, the generated IL will call当您不提供参数时,生成的 IL 将调用

OpCodes.Initobj Field OpCodes.Initobj 字段

Initializes each field of the value type at a specified address to a null reference or a 0 of the appropriate primitive type.将指定地址的值类型的每个字段初始化为 null 引用或相应原始类型的 0。

Furthermore此外

Unlike Newobj, initobj does not call the constructor method.与 Newobj 不同,initobj 不调用构造函数方法。 Initobj is intended for initializing value types, while newobj is used to allocate and initialize objects. Initobj 用于初始化值类型,而 newobj 用于分配和初始化对象。

In contrast, in the following case it will.相反,在以下情况下它会。 Defaults will be initalized the way you expect.默认值将以您期望的方式初始化。

var asd = new ReadonlyStruct(2);

The following generated IL will be以下生成的 IL 将是

newobj instance void ReadonlyStruct::.ctor(int32, uint8, bool)

OpCodes.Newobj Field OpCodes.Newobj 字段

The newobj instruction allocates a new instance of the class associated with ctor and initializes all the fields in the new instance to 0 (of the proper type) or null references as appropriate. newobj 指令分配与 ctor 关联的 class 的新实例,并将新实例中的所有字段初始化为 0(适当类型)或 null 引用。 It then calls the constructor ctor with the given arguments along with the newly created instance.然后,它使用给定的 arguments 以及新创建的实例调用构造函数 ctor。 After the constructor has been called, the now initialized object reference (type O) is pushed on the stack.调用构造函数后,将现在初始化的 object 引用(类型 O)压入堆栈。

In short, you may need to rethink your problem or use a static create method.简而言之,您可能需要重新考虑您的问题或使用 static 创建方法。

Optional parameters in C# always work this way. C# 中的可选参数始终以这种方式工作。 For any given call, if there are two overloads that are applicable, and one requires the compiler to use the default value as the argument for a parameter and the other doesn't, the one that doesn't require it "wins".对于任何给定的调用,如果有两个适用的重载,一个要求编译器使用默认值作为参数的参数,而另一个没有,则不需要它的那个“获胜”。 Here's a simple example of that:这是一个简单的例子:

using System;

class Test
{
    static void Main()
    {
        // Prints "Parameterless"
        Foo();
    }

    static void Foo() =>
        Console.WriteLine("Parameterless");

    static void Foo(int x = 0) =>
        Console.WriteLine("Parameterized");
}

Next, remember that every struct implicitly has a parameterless constructor.接下来,请记住每个结构都隐式地具有一个无参数构造函数。 From the C# 5 ECMA standard, section 16.4.9:来自 C# 5 ECMA 标准,第 16.4.9 节:

Unlike a class, a struct is not permitted to declare a parameterless instance constructor.与 class 不同,不允许结构声明无参数实例构造函数。 Instead, every struct implicitly has a parameterless instance constructor, which always returns the value that results from setting all value type fields to their default value and all reference type fields to null .相反,每个结构都隐含了一个无参数的实例构造函数,它总是返回将所有值类型字段设置为其默认值并将所有引用类型字段设置为null的值。

Put those two facts together, and the behaviour you're seeing makes perfect sense.把这两个事实放在一起,你看到的行为就很合理了。 The parameterless constructor is used in preference to the parameterized one, when you don't specify any argument.当您不指定任何参数时,无参数构造函数优先于参数化构造函数。 You can see exactly the same thing with a class, where the parameterless constructor is explicit:您可以使用 class 看到完全相同的内容,其中无参数构造函数是显式的:

using System;

class Test
{
    static void Main()
    {
        // Prints "Parameterless"
        Foo f = new Foo();
    }
}

class Foo
{
    public Foo()
    {
        Console.WriteLine("Parameterless");
    }

    public Foo(int x = 0, int y = 0)
    {
        Console.WriteLine("Parameterized");
    }
}

So what you're seeing is the C# language being entirely consistent.所以你看到的是 C# 语言完全一致。 It may not be the behaviour you want , but I believe it's behaviour that makes perfect sense.这可能不是您想要的行为,但我相信这是非常有意义的行为。

Note that if you specify any arguments, eg new Foo(x: 0) , then the parameterized overload will be chosen, and the defaults will be used for any parameter without a corresponding argument.请注意,如果您指定任何arguments,例如new Foo(x: 0) ,则将选择参数化重载,并且默认值将用于任何没有相应参数的参数。

As you've said elsewhere, the way to work around this is to declare a static method with optional parameters, that doesn't have a parameterless overload.正如您在其他地方所说,解决此问题的方法是声明一个带有可选参数的 static 方法,该方法没有无参数重载。 That way, the same method will be called whichever arguments are provided - and that can then call the constructor.这样,无论提供了 arguments,都会调用相同的方法 - 然后可以调用构造函数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2025 STACKOOM.COM