c# - 对象实例正在覆盖另一个实例,但我不明白为什么

标签 c# object constructor instances

我想知道你是否可以看看我的代码并看看我哪里出了问题。基本上,我创建了一个“UserFile”类型的对象(我自己的对象类型),并创建了它的 2 个实例,在该对象的构造函数中,我调用了一个静态类方法。除了在调用对象构造函数后第二个实例覆盖第一个实例之外,一切都很顺利。我已经完成了该程序,但我感到非常困惑。我感觉我在这里错过了一些非常明显的东西。

这是表单上用于创建实例的按钮

    private void btnCompare_Click(object sender, EventArgs e)
    {
        if (lstFiles.CheckedItems.Count == 2)
        {
            file1 = new UserFile(((FileLocation)lstFiles.CheckedItems[0]).filePath);

            file2 = new UserFile(((FileLocation)lstFiles.CheckedItems[1]).filePath);
        }
    }

这是带有构造函数的 UserFile 类

public class UserFile
{
    public Dictionary<int,Individual> fileIndividuals;
    public Dictionary<int, Family> fileFamilies;
    public Header fileHead;

    public UserFile(string _dir)
    {
        fileIndividuals = new Dictionary<int, Individual>();
        fileFamilies = new Dictionary<int, Family>();
        fileHead = new Header();
        ReadFromFile.Read(_dir);
        fileIndividuals = ReadFromFile.individuals;
        fileFamilies = ReadFromFile.families;
        fileHead = ReadFromFile.head;
    }
}

这是 UserFile 类调用的 ReadFromFile 方法

static class ReadFromFile
{
    public static string filename = "";

    public static Header head;
    public static Individual individual;
    public static Dictionary<int, Individual> individuals = new Dictionary<int, Individual>();

    public static Family family;
    public static Dictionary<int, Family> families = new Dictionary<int, Family>();

    public static GedcomRecordEnum currentRecord = GedcomRecordEnum.None;
    public static GedcomSubRecordEnum currentFirstLvlRecord = GedcomSubRecordEnum.None;
    public static GedcomSecondLevelEnum currentSecondLvlRecord = GedcomSecondLevelEnum.None;

    static public void Read(string fileName)
    {
        individuals.Clear();
        families.Clear();
        head = null;
        if (File.Exists(fileName))
        {
            filename = fileName;
            StreamReader reader = new StreamReader(fileName);

            while (!reader.EndOfStream)
            {
                string currentLine = reader.ReadLine();
                Match m = Regex.Match(currentLine, "(?<index>[0-9]) (?<keyword>[A-Z_@0-9]+)(?: *)(?<detail>.*)");

                string debug = m.Groups["index"].ToString();

                switch (m.Groups["index"].ToString())
                {
                    case "0":
                        ProcessRootLevel(m.Groups["keyword"].ToString());
                        break;
                    case "1":
                        ProcessLevel1(m.Groups["keyword"].ToString(), m.Groups["detail"].ToString());
                        break;
                    case "2":
                        ProcessLevel2(m.Groups["keyword"].ToString(), m.Groups["detail"].ToString());
                        break;
                    case "3":
                        ProcessLevel3(m.Groups["keyword"].ToString(), m.Groups["detail"].ToString());
                        break;
                }
            }
            reader.Close();
        }
    }
}

最佳答案

问题是 ReadFromFile 类上的以下静态属性,如果我假设“覆盖”意味着 UserFile 的两个实例都指向相同的数据:

 public static Dictionary<int, Family> families = new Dictionary<int, Family>();
 public static Dictionary<int, Individual> individuals = new Dictionary<int, Individual>();
 public static Header head;

问题在于 UserFile 的构造函数对 static 属性的使用。

    ReadFromFile.Read(_dir);
    fileIndividuals = ReadFromFile.individuals; // <-- Uh-oh!
    fileFamilies = ReadFromFile.families;       // <-- Uh-oh!
    fileHead = ReadFromFile.head;               // <-- Uh-oh!

这里发生的事情是成员变量fileIndividualsfileFamiliesfileHead被设置为引用静态 ReadFromFile 类上的 individualsfamilieshead 属性,不是副本 (因为它们是类而不是值类型)。因此,下次调用 ReadFromFile.Read() 时,ReadFromFile 上的静态属性将被更新(覆盖),但 UserFile 的先前实例只是指向相同静态属性,因此file1file2将具有相同的数据。

那么你会如何解决这个问题呢?两种选择:

  1. 创建ReadFromFile 和实例类,而不是静态类。在 UserFile 构造函数中构造一个新实例,并且不要使用任何静态属性。
  2. UserFile 的构造函数中复制individualsfamilieshead 中的数据。 “foreach”遍历每个项目,并将其复制到新字典中。

简单解释:

当您执行赋值(C# 中的 = 字符)时,如果对象是一个类,则目标将被分配一个指向右侧的“指针”(引用)。如果它是值类型,则会被复制。 Dictionary 是一个类,因此您得到的是指针而不是副本。

代码说明:

public static class MyStaticClass
{
     public static List<string> MyList = new List<string> 
}

其他地方...

public void MyMethod()
{
    List<string> myList1 = MyStaticClass.MyList; 
    List<string> myList2 = MyStaticClass.MyList; 

    myList1.Add("Hello");  // Add to first list
    myList2.Add("World");  // Add to second list 

    foreach(string item in myList1) // print all items in the second list
    {
         Console.WriteLine("List 1: " + item); 
    }

    foreach(string item in myList2) // print all items in the second list
    {
         Console.WriteLine("List 2: " + item); 
    }
} 

其输出将是:

List 1: Hello
List 1: World
List 2: Hello
List 2: World 

但是为什么呢?我们仅将“World”添加到 myList2 中。那么 myList1myList2 指向同一件事。当我们执行 myList1 = MyStaticClass.MyList 时,我们没有获得该项目的副本,只是获得了它的引用

关于c# - 对象实例正在覆盖另一个实例,但我不明白为什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9881309/

相关文章:

c# - Linux:Mono 安装和错误

c# - EF Core 查询 Where 子句是一个集合?

java - 在父 Activity 中初始化的类对象在子 Activity 中重新初始化

java - 引用对象的对象的对象

java - 如何从枚举构造函数中抛出异常?

java - 为什么子类必须先调用父类(super class)的构造函数才能初始化自己的变量

c# - 我的 StreamReader 代码只读取每隔一行 c#

c# - 将 X.509 证书发送到 WCF c#

c++ - 成员变量不保存从函数成员添加到它的值

javascript - JavaScript 中函数声明的区别