C# 奇怪的对象行为

发布于 2024-12-06 21:35:19 字数 1693 浏览 2 评论 0原文

在处理自定义对象时,我注意到 C# 中的一些东西有点奇怪。我确信这只是我缺乏理解,所以也许有人可以启发我。

如果我创建一个自定义对象,然后将该对象分配给另一个对象的属性,并且第二个对象修改分配给它的对象,则这些更改将反映在进行分配的同一类中,即使没有返回任何内容。

你想要英语的吗?这是一个例子:

class MyProgram
{
    static void Main()
    {
        var myList = new List<string>();
        myList.Add("I was added from MyProgram.Main().");
        var myObject = new SomeObject();
        myObject.MyList = myList;
        myObject.DoSomething();

        foreach (string s in myList)
            Console.WriteLine(s); // This displays both strings.
    }
}

public class SomeObject
{
    public List<string> MyList { get; set; }

    public void DoSomething()
    {
        this.MyList.Add("I was added from SomeObject.DoSomething()");
    }
}

在上面的示例中,我本以为,因为 SomeObject.DoSomething() 返回 void,所以该程序只会显示 “I was added from MyProgram.Main(). “。然而,List 实际上包含该行和“I was added from SomeObject.DoSomething()”

这是另一个例子。在此示例中,字符串保持不变。有什么区别,我错过了什么?

class MyProgram
{
    static void Main()
    {
        var myString = "I was set in MyProgram.Main()";
        var myObject = new SomeObject();
        myObject.MyString = myString;
        myObject.DoSomething();

        Console.WriteLine(myString); // Displays original string.
    }
}

public class SomeObject
{
    public string MyString { get; set; }

    public void DoSomething()
    {
        this.MyString = "I was set in SomeObject.DoSomething().";
    }
}

该程序示例最终显示“I was set in MyProgram.Main()”。看到第一个示例的结果后,我会假设第二个程序会用 “I was set in SomeObject.DoSomething().” 覆盖该字符串。我想我一定是误会了什么。

I noticed something in C# when dealing with custom objects that I found to be a little odd. I am certain it is just a lack of understanding on my part so maybe someone can enlighten me.

If I create a custom object and then I assign that object to the property of another object and the second object modifies the object assigned to it, those changes are reflected in the same class that did the assigning even though nothing is returned.

You want that in English? Here is an example:

class MyProgram
{
    static void Main()
    {
        var myList = new List<string>();
        myList.Add("I was added from MyProgram.Main().");
        var myObject = new SomeObject();
        myObject.MyList = myList;
        myObject.DoSomething();

        foreach (string s in myList)
            Console.WriteLine(s); // This displays both strings.
    }
}

public class SomeObject
{
    public List<string> MyList { get; set; }

    public void DoSomething()
    {
        this.MyList.Add("I was added from SomeObject.DoSomething()");
    }
}

In the above sample I would have thought that, because SomeObject.DoSomething() returns void, this program would only display "I was added from MyProgram.Main().". However, the List<string> in fact contains both that line and "I was added from SomeObject.DoSomething()".

Here is another example. In this example the string remains unchanged. What is the difference and what am I missing?

class MyProgram
{
    static void Main()
    {
        var myString = "I was set in MyProgram.Main()";
        var myObject = new SomeObject();
        myObject.MyString = myString;
        myObject.DoSomething();

        Console.WriteLine(myString); // Displays original string.
    }
}

public class SomeObject
{
    public string MyString { get; set; }

    public void DoSomething()
    {
        this.MyString = "I was set in SomeObject.DoSomething().";
    }
}

This program sample ends up displaying "I was set in MyProgram.Main()". After seeing the results of the first sample I would have assumed that the second program would have overwritten the string with "I was set in SomeObject.DoSomething().". I think I must be misunderstanding something.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

鲜血染红嫁衣 2024-12-13 21:35:19

这并不奇怪,也不奇怪。创建类时,您创建了引用类型。当您传递对对象的引用时,对它们所引用的对象的修改对于持有该对象引用的任何人都是可见的。

var myList = new List<string>();
myList.Add("I was added from MyProgram.Main().");
var myObject = new SomeObject();
myObject.MyList = myList;
myObject.DoSomething();

因此,在此代码块中,您实例化 List 的新实例,并将对该实例的引用分配给变量 myList。然后将 "I was added from MyProgram.Main()." 添加到 myList 引用的列表中。然后,您将对同一列表的引用分配给 myObject.MyList (明确地说,myListmyObject.MyList 都引用相同的列表) List!然后调用 myObject.DoSomething() ,将 "I was added from SomeObject.DoSomething()" 添加到myObject.MyList。由于 myListmyObject.MyList 都引用相同的 List,因此它们打个比方

,我有一张写有电话号码的纸,我把它复印给你。现在我拨打那个号码。并告诉线路另一端的人在他们的房子上挂一个横幅,上面写着“我是从 MyProgram.Main() 添加的。”在他们的房子上挂上横幅,上面写着“我是从 SomeObject.DoSomething() 添加的”。好吧,住在有该电话号码的房子里的人现在在他们的房子外面会有两条横幅。一个说

I was added from MyProgram.Main().

,另一个说

I was added from SomeObject.DoSomething()

有道理吗?

现在,在第二个示例中,情况有点棘手。

var myString = "I was set in MyProgram.Main()";
var myObject = new SomeObject();
myObject.MyString = myString;
myObject.DoSomething();

首先创建一个新的字符串,其值为“I was set in MyProgram.Main()”,并将对该字符串的引用分配给myString >。然后将对同一字符串的引用分配给 myObject.MyString。同样,myStringmyObject.MyString 都引用了同一个 string,其值为 "I was set in MyProgram.Main( )”。但是然后你调用 myObject.DoSomething ,其中有这样有趣的行

this.MyString = "I was set in SomeObject.DoSomething().";

好吧,现在你已经创建了一个新的字符串,其值为 "I was set in SomeObject.DoSomething ()." 并将对该字符串的引用分配给myObject.MyString。请注意,您从未更改过 myString 所保存的引用。所以现在,myStringmyObject.MyString 引用了不同的字符串!

我们再类比一下。我有一张纸,上面有网址。我把那张纸复印下来给你。我们都有一张纸,上面有相同的网址。您划掉该网址并写下另一个地址。它不会影响我在纸上看到的内容!

最后,这个线程中的很多人都在抱怨 string 的不变性。这里发生的事情与string的不变性无关。

This isn't odd, or strange. When you create a class, you create reference type. When you pass references to objects around, modifications to the objects they refer to are visible to anyone that holds a reference to that object.

var myList = new List<string>();
myList.Add("I was added from MyProgram.Main().");
var myObject = new SomeObject();
myObject.MyList = myList;
myObject.DoSomething();

So in this block of code, you instantiate a new instance of List<string> and assign a reference to that instance to the variable myList. Then you add "I was added from MyProgram.Main()." to the list referred to by myList. Then you assign a refernce to that same list to myObject.MyList (to be explicit, both myList and myObject.MyList are referring to the same List<string>! Then you invoke myObject.DoSomething() which adds "I was added from SomeObject.DoSomething()" to myObject.MyList. Since both myList and myObject.MyList are referring to the same List<string>, they will both see this modification.

Let's go by way of analogy. I have a piece of paper with a telephone number on it. I photocopy that piece of paper and give it to you. We both have a piece of paper with the same telephone number on it. Now I call up that number and tell the person on the other end of the line to put a banner up on their house that says "I was added from MyProgram.Main()." You call up the person on the other end of the line to put a banner up on their house that says "I was added from SomeObject.DoSomething()". Well, the person who lives at the house that has that telephone number is now going to have two banners outside their house. One that says

I was added from MyProgram.Main().

and another that says

I was added from SomeObject.DoSomething()

Make sense?

Now, in your second example, it's a little trickier.

var myString = "I was set in MyProgram.Main()";
var myObject = new SomeObject();
myObject.MyString = myString;
myObject.DoSomething();

You start by creating a new string whose value is "I was set in MyProgram.Main()" and assign a reference to that string to myString. Then you assign a reference to that same string to myObject.MyString. Again, both myString and myObject.MyString are referring to that same string whose value is "I was set in MyProgram.Main()". But then you invoke myObject.DoSomething which has this interesting line

this.MyString = "I was set in SomeObject.DoSomething().";

Well, now you've created a new string whose value is "I was set in SomeObject.DoSomething()." and assign a reference to that string to myObject.MyString. Note that you never changed the reference that myString holds. So now, myString and myObject.MyString are referring to different strings!

Let's go by analogy again. I have a piece of paper with a web address on it. I photocopy that piece of paper and give it to you. We both have a piece of paper with the same web address on it. You cross out that web address and write down a different address. It doesn't affect what I see on my piece of paper!

Finally, a lot of people in this thread are yammering about the immutability of string. What is going on here has nothing to do with the immutability of string.

梦里南柯 2024-12-13 21:35:19

这是绝对正确的:

myObject.MyList = myList; 

这一行将 myList 的引用分配给 myObject 的属性。
为了证明这一点,请在 myListmyObject.MyList 上调用 GetHashCode()

如果您愿意,我们正在讨论指向同一内存位置的不同指针。

It's absolutely correct:

myObject.MyList = myList; 

This line assign a reference of myList to the myObject's property.
To prove this this, call GetHashCode() on myList and on myObject.MyList.

we are talking about different pointers to same memory location, if you wish.

云之铃。 2024-12-13 21:35:19

方法是否返回某些内容与其内部发生的情况无关。
您似乎对作业的实际含义感到困惑。

让我们从头开始吧。

var myList = new List<string>();

在内存中分配一个新的 List 对象,并将对其的引用放入 myList 变量中。
目前,您的代码仅创建一个 List 实例,但您可以在不同位置存储对其的引用

var theSameList = myList; 
var sameOldList = myList;
someObject.MyList = myList;

现在 myListtheSameListsameOldListsomeObject.MyList (依次存储在私有字段中SomeObject 由编译器自动生成)全部引用同一个对象

看一下这些:

var bob = new Person();
var guyIMetInTheBar = bob;
alice.Daddy = bob;
harry.Uncle = bob;
itDepartment.Head = bob;

只有一个 Person 实例,并且有许多对它的引用。
如果我们的鲍勃长大一岁,每个实例的年龄也会增加,这是很自然的事情。
这是同一个对象。

如果一座城市被重命名,您会期望所有地图都以新名称重新打印。

你觉得奇怪的是

这些更改反映在进行分配的同一个类中

,但是等等,更改没有反映。幕后没有复制。它们就在那里,因为它是同一个对象,如果你改变它,无论你从哪里访问它,你都会访问它的当前状态。

,将项目添加到列表中的位置并不重要:只要您引用同一个列表,您就会看到正在添加的项目。

因此 你的第二个例子,我看到杰森已经为你提供了 更好的解释超出了我所能提供的范围,所以我不会深入讨论。

就足够了

  1. 如果我说:字符串在 .NET 中是不可变的,由于各种原因您无法修改 string 的实例,
  2. 即使它们是可变的(例如 List 其内部状态可通过方法修改),在第二个示例中,您不会更改对象,而是会更改对象。更改参考

    var goodGuy = jack;
    爱丽丝.情人=杰克;
    爱丽丝.情人 = 迈克;
    

alice 的情绪变化会让 jack 变成坏人吗?当然不是。
同样,更改 myObject.MyString 不会影响局部变量 myString。您不对字符串本身执行任何操作(事实上,您不能)。

Whether or not a method returns something, has nothing to do with what happens inside it.
You seem to be confused regarding what assignment actually means.

Let's start from the beginning.

var myList = new List<string>();

allocates a new List<string> object in memory and puts a reference to it into myList variable.
There is currently just one instance of List<string> created by your code but you can store references to it in different places.

var theSameList = myList; 
var sameOldList = myList;
someObject.MyList = myList;

Right now myList, theSameList, sameOldList and someObject.MyList (which is in turn stored in a private field of SomeObject automagically generated by compiler) all refer to the same object.

Have a look at these:

var bob = new Person();
var guyIMetInTheBar = bob;
alice.Daddy = bob;
harry.Uncle = bob;
itDepartment.Head = bob;

There is just one instance of Person, and many references to it.
It's only natural that if our Bob grew a year older, each instance's Age would have increased.
It's the same object.

If a city was renamed, you'd expect all maps to be re-printed with its new name.

You find it strange that

those changes are reflected in the same class that did the assigning

—but wait, changes are not reflected. There's no copying under the hood. They're just there, because it's the same object, and if you change it, wherever you access it from, you access its current state.

So it matters not where you add an item to the list: as long as you're referring to the same list, you'll see the item being added.

As for your second example, I see Jason has already provided you with a much better explanation than I could possibly deliver so I won't go into that.

It will suffice if I say:

  1. Strings are immutable in .NET, you can't modify an instance of string for a variety of reasons.
  2. Even if they were mutable (like List<T> that has its internal state modifiable via methods), in your second example, you're not changing the object, you're changing the reference.

    var goodGuy = jack;
    alice.Lover = jack;
    alice.Lover = mike;
    

Would alice's change of mood make jack a bad guy? Certainly not.
Similarly, changing myObject.MyString doesn't affect local variable myString. You don't do anything to the string itself (and in fact, you can't).

溺ぐ爱和你が 2024-12-13 21:35:19

您混淆了两种类型的对象。
List 是字符串类型的列表.. 这意味着它可以接受字符串:)

当您调用 Add 方法时,它将字符串文字添加到其字符串集合中。

当您调用 DoSomething() 方法时,它可以使用与 Main 中相同的列表引用。因此,当您在控制台中打印时,您可以看到这两个字符串。

You are confusing both type of objects.
A List is a List of type string .. which means it can take strings :)

When you call the Add method it adds the string literal to its collection of strings.

At the time you call your DoSomething() method, the same list reference is available to it as the one you had in Main. Hence you could see both strings when you printed in the console.

养猫人 2024-12-13 21:35:19

不要忘记,您的变量也是对象。在第一个示例中,您创建一个 List<>对象并将其分配给您的新对象。您仅持有对列表的引用,在本例中,您现在持有对同一列表的两个引用。

在第二个示例中,您将特定的字符串对象分配给您的实例。

Don't forget, that your variables are objects too. In the first example, you create a List<> object and assign it to your new object. You only hold a reference to a list, in this case, you now hold two references to the same list.

In the second example you assign a specific string object to your instance.

三人与歌 2024-12-13 21:35:19

亚历克斯 - 你写的 -

在上面的示例中,我本以为,因为 SomeObject.DoSomething() 返回 void,所以该程序只会显示“我是从 MyProgram.Main() 添加的。”。然而,列表实际上包含该行和“我是从 SomeObject.DoSomething() 添加的”。

事实并非如此。函数的VOID只是意味着该函数没有返回值。这与您在 DoSomething() 方法中调用的 this.MyList.Add 方法无关。您必须引用同一个对象 - myList 和 SomeObject 中的 MyList。

Alex - you wrote -

In the above sample I would have thought that, because SomeObject.DoSomething() returns void, this program would only display "I was added from MyProgram.Main().". However, the List in fact contains both that line and "I was added from SomeObject.DoSomething()".

This is not the case. The VOID of the function just means the function does not return a value. This has nothing to do with the this.MyList.Add method you are invoking in the DoSomething() method. You do have to references to the same object - myList and the MyList in the SomeObject.

枯寂 2024-12-13 21:35:19

这是引用类型的行为方式,也是预期的。 myList 和 myObject.MyList 是对堆内存中同一个 List 对象的引用。

在第二个示例中,字符串是不可变的并且按值传递,因此在行

myObject.MyString = myString;

The Contents of myString are copied to myObject.MyString (即通过 value 传递,而不是通过 >引用

字符串有点特殊,因为它是一种引用类型和一种具有不变性特殊属性的值类型(一旦创建了一个字符串,就不能更改它,只能创建一个新字符串,但这是实施过程中对您有所隐藏)

This is how reference types behave and is expected. myList and myObject.MyList are references to the same List object in heap memory.

In the second example strings are immutable and are passed by value, so on the line

myObject.MyString = myString;

The contents of myString are copied to myObject.MyString (i.e. passed by value not by reference)

String is a bit special because it is a reference type and a value type with the special property of immutability (once you have created a string you can't change it only make a new one, but this is somewhat hidden from you by the implementation)

感情废物 2024-12-13 21:35:19

在第一个示例中......您正在使用可变对象,并且它始终通过引用来访问。 不同对象中对 MyList 的所有引用都指向同一事物

在另一种情况下,字符串的行为略有不同。声明字符串文字(即引号之间的文本)会创建一个新的字符串实例,与原始版本完全分离。 您不能修改字符串,只能创建一个新字符串

更新

杰森是对的,它与字符串不变性无关......但是......
我不禁认为字符串不变性在这里有它的用处。不在这个具体示例中,但如果 SomeObject.DoSomething 的代码是这样的: this.MyString += "I was Updated in SomeObject.DoSomething()."; ,那么你必须解释新的字符串是通过“串联”创建的,并且第一个字符串没有更新

In the first example ... you are working with an mutable objects, and it is always accessed by reerence. All references to MyList in different objects refer to the same thing.

In the other case, strings behave a bit differently. Declaring a string literal (i.e. text between quotes) creates a new instance of a String, completely separated from the original version. You CAN NOT modify a string, just create a new one.

UPDATE

Jason is right, it has nothing to do with String immutability ... but ....
I can't help but think that string immutabiity has its word in here. Not in THIS concrete example, but if SomeObject.DoSomething's code was this : this.MyString += "I was updated in SomeObject.DoSomething()."; , then you would have to explain that new String is created by the "concatenation", and the first string is not updated

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文