Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/335.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 结构是否可以包含引用类型的字段_C#_Struct_Reference Type - Fatal编程技术网

C# 结构是否可以包含引用类型的字段

C# 结构是否可以包含引用类型的字段,c#,struct,reference-type,C#,Struct,Reference Type,结构是否可以包含引用类型的字段?如果可以,这是一种不好的做法吗 是的,他们可以 视情况而定 许多人认为结构应该是不可变的,在这种情况下,持有对对象的引用可能意味着它不是 但这要视情况而定。这是肯定的,而且这样做并不是不好的做法 struct Example { public readonly string Field1; } 只读不是必需的,但使结构不可变是一种很好的做法。是的,这是可能的,是的,这通常是一种不好的做法 如果您查看.NET framework本身,您将看到几乎所有结构都只包

结构是否可以包含引用类型的字段?如果可以,这是一种不好的做法吗

是的,他们可以

视情况而定

许多人认为结构应该是不可变的,在这种情况下,持有对对象的引用可能意味着它不是


但这要视情况而定。

这是肯定的,而且这样做并不是不好的做法

struct Example {
  public readonly string Field1;
}

只读不是必需的,但使结构不可变是一种很好的做法。

是的,这是可能的,是的,这通常是一种不好的做法


如果您查看.NET framework本身,您将看到几乎所有结构都只包含基本值类型。

是的,它们可以。这是个好主意吗?那要看情况而定。就我个人而言,我很少首先创建自己的结构。。。我会以某种程度的怀疑态度对待任何新的用户定义结构。我并不是说它总是一个错误的选择,只是它需要一个更清晰的论证而不是一个类

对于一个结构来说,引用一个可变的对象是一个坏主意。。。否则,可以有两个看起来独立但不独立的值:

MyValueType foo = ...;
MyValueType bar = foo; // Value type, hence copy...

foo.List.Add("x");
// Eek, bar's list has now changed too!

可变结构是邪恶的。引用可变类型的不可变结构在不同方面都是邪恶的。

不能拥有可变结构的原因是引用类型的行为。阅读本文:

当您有一个包含对象的结构(任何不是int或double这样的基元的东西)并复制该结构的实例时,内部的对象不会被“深度”复制,因为它只是对包含实际类的内存位置的引用(指针)。因此,如果您复制一个包含类实例的可变结构,那么该副本将引用与原始实例相同的实例(因此上面更改了bar的列表)


如果你一定要让这个结构是可变的,那就让里面的任何类实例都是只读的,或者——这是一个糟糕的做法——试着确保你永远不会复制这个结构。

因为这得到了否决票,我试着重写一下,看看它是否能变得更清晰。这个问题由来已久;但是很好!我最近还看到了一些详细阐述这一点的链接

我想补充的一点是,如果您声明引用字段,您必须能够在自己的块之外进行推理:当有人使用您的结构时。我添加的具体点实际上只是关于声明结构的只读字段;但在这种情况下,结构中的字段可以更改其结果;这很难解释

我看到了这个链接,程序员在其中声明了一个包含
只读
结构
字段的类。他的类中的字段是一个
结构
——它是一个
链接列表。枚举器
——由于该字段是
只读的
——他自己的类方法得到了枚举器结构的副本,状态是复制的,而不是动态的

但是,如果您继续并通过简单地从struct字段中删除
只读
来修复他的代码(这是可行的);然而,如果您决定创建自己的类a
struct
,那么现在,您的结构的使用者不能将其用作只读字段,否则他们会被同样的问题所困扰。(如果这似乎是人为的,因为您没有只读枚举器,那么如果它支持重置,您实际上可能会这样做!)

因此,如果这不是一个最清晰的例子,我想说的是,你可以对自己的实现进行推理,但是如果你是一个结构体,你还需要对复制你的价值的消费者以及他们将得到什么进行推理

下面是我找到的例子

他的类不是
结构
,但确实包含m_枚举器字段(程序员应该知道它是
结构

事实证明,该类的方法得到了该值的副本,并且不起作用实际上,您可以非常仔细地检查此块以了解这一点

您可以通过将字段设置为非
只读
——来解决此问题,因为该字段已经指向令人困惑的地方。但您也可以通过将字段声明为
接口
类型--
IEnumerator
来修复它

但是,如果您将字段声明为
struct
,并声明它不是
readonly
,然后选择将类定义为
struct
,那么现在如果有人在某个类中将
struct
的实例声明为
readonly
字段,那么他们又会丢失

例如:

公共类程序
{
私有结构枚举器包装器:IEnumerator
{
//总是失败---本地方法读取只读结构并获取副本
//私有只读链接列表。枚举器m_枚举器;
//修正了一个:---在本地,方法不再获得副本;
//但是如果这个结构的使用者创建了一个只读字段,那么他们也会这样做
//始终获取此字段的副本,并且此字段包含此结构字段的副本!
私有链接列表。枚举器m_枚举器;
//两者都解决了!!
//因为这不是一个值类型,所以即使这个结构的使用者
//只读复制,始终读取内存指针而不是值
//私有IEnumerator m_枚举器;
公共枚举器包装器(LinkedList LinkedList)
=>m_枚举器=linkedList.GetEnumerator();
公共电流
=>m_枚举器.Current;
对象System.Collections.IEnumerator.Current
=>电流;
公共图书馆
=>m_枚举数。MoveNext();
公共无效重置()
=>((System.Collections.IEnumerator)m_枚举器).Reset();
公共空间处置()
=>m_枚举器.Dispose();
}
private readonly LinkedList l=新建LinkedList();
私有只读枚举器;
公共项目
public class Program
{
    private struct EnumeratorWrapper : IEnumerator<int>
    {
        // Fails always --- the local methods read the readonly struct and get a copy
        //private readonly LinkedList<int>.Enumerator m_Enumerator;

        // Fixes one: --- locally, methods no longer get a copy;
        // BUT if a consumer of THIS struct makes a readonly field, then again they will
        // always get a copy of THIS, AND this contains a copy of this struct field!
        private LinkedList<int>.Enumerator m_Enumerator;

        // Fixes both!!
        // Because this is not a value type, even a consumer of THIS struct making a
        // readonly copy, always reads the memory pointer and not a value
        //private IEnumerator<int> m_Enumerator;


        public EnumeratorWrapper(LinkedList<int> linkedList) 
            => m_Enumerator = linkedList.GetEnumerator();


        public int Current
            => m_Enumerator.Current;

        object System.Collections.IEnumerator.Current
            => Current;

        public bool MoveNext()
            => m_Enumerator.MoveNext();

        public void Reset()
            => ((System.Collections.IEnumerator) m_Enumerator).Reset();

        public void Dispose()
            => m_Enumerator.Dispose();
    }


    private readonly LinkedList<int> l = new LinkedList<int>();
    private readonly EnumeratorWrapper e;


    public Program()
    {
        for (int i = 0; i < 10; ++i) {
            l.AddLast(i);
        }
        e = new EnumeratorWrapper(l);
    }


    public static void Main()
    {
        Program p = new Program();

        // This works --- a local copy every time
        EnumeratorWrapper e = new EnumeratorWrapper(p.l);
        while (e.MoveNext()) {
            Console.WriteLine(e.Current);
        }

        // This fails if the struct cannot support living in a readonly field
        while (p.e.MoveNext()) {
            Console.WriteLine(p.e.Current);
        }
        Console.ReadKey();
    }
}