【问题标题】:Problem with struct and property in c#c#中结构和属性的问题
【发布时间】:2010-09-16 19:05:00
【问题描述】:

在一个文件中我定义了一个公共结构

public struct mystruct
{
    public Double struct1;
    public Decimal struct2;
}

在另一个我尝试这样做:

class Test
{
    mystruct my_va;

    public mystruct my_va
    {
        get { return my_va; }
        set { my_va = value; }
    }

    public Test()
    {
        my_va.struct1 = 10;
    }
}

Intellisense 识别 My_va.struct1 但编译器说

错误1 无法修改返回值 'TEST.mystruct' 因为它不是 变量

如何改正语法?

【问题讨论】:

    标签: c# struct


    【解决方案1】:

    是的,完全正确。您会看到,当您获取My_va 时,您正在获取一个值——my_va 的当前值的副本。更改该值不会有任何好处,因为您将立即丢弃副本。编译器阻止你编写看起来不像它的代码。

    一般来说,避免使用可变结构。他们是邪恶的。在这种情况下,您可以(例如)将mystruct 更改为不可变,但使用如下方法:

    public mystruct WithStruct1(double newValue)
    {
        return new mystruct(newValue, struct2);
    }
    

    然后将您的构造函数代码更改为:

    My_va = My_va.WithStruct1(10);
    

    ...尽管在 this 的情况下(假设您在构造函数中)您应该编写的可能性要大得多:

    My_va = new mystruct(10, 0);
    

    结构不仅应该是不可变的,而且在大多数代码库中它们应该是非常罕见的,IMO。除了Noda Time,我几乎没有编写过自己的自定义值类型。

    最后,请学习.NET naming conventions并尝试关注他们,即使是示例代码:)

    【讨论】:

    • 我曾经听说过一个可变结构烧毁整个村庄的故事。太疯狂了。
    • @Anthony:你“曾经听过一个故事”?你说得好像你不相信一样。我在那里,看到了这一切。它在晚上仍然困扰着我。
    • Anakin Skywalker 很酷,直到他了解了可变结构。
    • 我曾经只用一把耙子和一大杯水打败了一个可变结构。
    • @Marc - 这取决于。 grue 有耙子和一大杯水吗?
    【解决方案2】:

    强烈建议避免使用可变结构。他们表现出各种令人惊讶的行为。

    解决方案:使您的结构不可变。

    public struct MyStruct
    {
        public readonly double Value1;
        public readonly decimal Value2;
    
        public MyStruct(double value1, decimal value2)
        {
            this.Value1 = value1;
            this.Value2 = value2;
        }
    }
    

    用法:

    class Test
    {
        private MyStruct myStruct;
    
        public Test()
        {
            myStruct = new MyStruct(10, 42);
        }
    
        public MyStruct MyStruct
        {
            get { return myStruct; }
            set { myStruct = value; }
        }
    }
    

    【讨论】:

    • @Caspar Kleijne:Value1 和 Value2 是字段,而不是自动属性。所以,没有。
    • 谢谢,但你能解释一下为什么你的语法有效而不是我的吗,我无法真正理解根本区别:为什么将其设为只读与设置属性值有关?跨度>
    • @user310291:正如 @Jon Skeet 所解释的,结构具有值语义,您实际上是在修改副本的字段,而不是您打算修改的结构值。这是一个常见的错误来源,因此即使是 C# 编译器也会对此发出警告。如果您使结构不可变(= 所有字段只读),则永远不会意外修改副本。 (顺便说一句,set 访问器与此无关;您的原始代码从未使用过它。)
    • 好吧,如果它只是一个警告,那没关系,但它不能让我这样做:)
    • 这里有一个简单的类比——int也是一个值类型。您可以像这样创建 int 值类型的实例 - int i = 1;。您可以将 i 的值更改为 2 (i = 2),但您永远不想将 1 的值更改为 2。
    【解决方案3】:

    我使用结构列表,并以不同的方式解决了这个问题。

    struct Pixel
    { Public int X;
      Public int C;
    }
    List<Pixel> PixelList = new List<Pixel>
    TempPixel = new Pixel();
    

    现在,当我想设置一个值时,我的代码如下:

    TempPixel = PixelList[i];
    TempPixel.X= 23;  // set some value
    PixelList[i] = TempPixel
    

    代码看起来可能有点奇怪,但它解决了问题。 它解决了结构体不能直接赋值单个值,但可以是相似类型的副本的问题。 解决错误 CS1612:

    https://msdn.microsoft.com/query/dev10.query?appId=Dev10IDEF1&l=EN-US&k=k%28CS1612%29;k%28TargetFrameworkMoniker-%22.NETFRAMEWORK%2cVERSION%3dV2.0%22%29;k%28DevLang-CSHARP%29&rd=true

    【讨论】:

    • 代码看起来一点也不奇怪。相反,如果一个人不能或不愿意使用一组结构,这是正确的方法。有些人不喜欢可变结构,因为它们的行为不像对象,但是在想要将一堆变量用胶带粘在一起的情况下,我会说最好使用一堆用胶带粘在一起的变量而不是尝试使结构表现为对一个对象的平庸模仿,该对象正在对用胶带粘在一起的一堆变量进行不良模仿。我要注意的一件事...
    • ...是不是把List&lt;Pixel&gt; PixelList换成Pixel[] Pixels = new Pixel[16]; /* Or some reasonable default */ int PixelCount;,加上void AddPixel(Pixel newPixel) { if (PixelCount &gt;= Pixels.Count) Pixels = Array.Resize(ref Pixels, PixelCount*2); Pixels[PixelCount] = newPixel; PixelCount++; }可能会更方便高效。这样做将允许就地更新数组元素。
    • 你的权利,我在一些“危险”的多线程代码中使用它,作为全局变量列表。我走出了危险区域,因为我的代码使用结构运行得非常快。我认为结构也不应该被大量使用,但在奇怪的情况下,我会在专用硬件上使用点云数据做一些奇怪的事情,那么它可能会更好;在使用结构之前,我建议人们查看他们是否真的需要它。速度和低内存消耗,可能是原因。
    • 恕我直言,如果列表中的每个项目都封装了一些经常使用的值,那么开放字段结构语义是正确的使用方法> 作为一个单元,但不应有任何身份作为一个单元。 Pixel[100] 将封装 100 个独立的 Decimal 值和 100 个独立的 double 值。如果Pixel 是一个可变类,Pixel[100] 可能封装 100+100 个独立值,但它不必如此。它可以保存对同一实例的 100 个引用。这增加了很多额外的潜在状态,而这些状态通常在语义上是可取的。
    【解决方案4】:

    最简单的修复:将结构更改为类。

    【讨论】:

      【解决方案5】:

      不幸的是,在分配给属性(即调用属性设置器)时可能会错误地生成此错误。不可变结构仍然可以具有有效的属性设置器,只要属性设置器实际上并未分配给结构中的任何字段。例如,

      public struct Relay
      {
          public Relay(Func<string> getText, Action<string> setText)
          {
              this.GetText = getText;
              this.SetText = setText;
          }
          private readonly Func<string> GetText;
          private readonly Action<string> SetText;
      
          public string Text {
              get { return this.GetText(); }
              set { this.SetText(value); }
          }
      }
      
      class Example
      {
          private Relay Relay {
              get { return new Relay(() => this.text, t => { this.text = t; }); }
          }
      
          private string text;
      
      
          public Method()
          {
              var r = new Relay();
              r.Text = "hello"; // not a compile error (although there is a null reference)
      
              // Inappropriately generates a compiler error
              this.Relay.Text = "hello";
      
              r = this.Relay;
              r.Text = "hello"; // OK
          }
      }
      

      【讨论】:

      • “错误”CS 1612 应该是一个警告。我如何才能将此反馈给 Microsoft 以便修复它?我不想使用一个类,因为显然这涉及到不适合我的情况的开销。
      【解决方案6】:

      值得注意的是,您可以通过以下方式克服这种行为:

      1. 通过结构实现接口:Struct : IStruct
      2. 将结构声明为字段:Struct strExplicitly;

      示例:

      public interface IStruct
      {
          int Age { get; set; }
      }
      
      public struct Struct : IStruct
      {
          public int Age { get; set; }
      }
      
      public class Test
      {
          IStruct strInterface { get; set; }
          Struct strExplicitly;
      
          public Test()
          {
              strInterface = new Struct();
              strExplicitly = new Struct();
          }
      
          public void ChangeAge()
          {
              strInterface.Age = 2;
              strExplicitly.Age = 2;
          }
      }
      

      【讨论】:

      • 这样编译通过了,但是注意接口会引入装箱,最好通过属性发布公共数据。
      猜你喜欢
      • 1970-01-01
      • 2013-05-17
      • 2021-11-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多