C# 创建对象原始引用的抓取包(集合)

C# 创建对象原始引用的抓取包(集合),c#,memory-management,collections,reference,C#,Memory Management,Collections,Reference,我正在寻找一种集合类型/方法,在这种类型/方法中,我可以将一个对象添加到一组对象中,然后分别更改该对象的属性,并将这些更改反映在集合中的对象中 我听说列表通过引用添加值,所以我认为引用将指向同一个对象。换句话说,我假设: List<string> valuesList = new List<string>(); string initialValue = "Alpha"; valuesList.Add(initialValue); initialValue = "Bra

我正在寻找一种集合类型/方法,在这种类型/方法中,我可以将一个对象添加到一组对象中,然后分别更改该对象的属性,并将这些更改反映在集合中的对象中

我听说列表通过引用添加值,所以我认为引用将指向同一个对象。换句话说,我假设:

List<string> valuesList = new List<string>();
string initialValue = "Alpha";
valuesList.Add(initialValue);

initialValue = "Bravo";
bool incorrectAssumption = (valuesList[0] == "Bravo");
列表值列表=新列表();
字符串initialValue=“Alpha”;
valuesList.Add(初始值);
initialValue=“布拉沃”;
布尔不正确假设=(值列表[0]=“B”);
我曾希望“valuesList”将包含新的值“Bravo”。尝试过之后,我意识到列表复制了引用,但没有吸收它,因此valueList仍然只有“Alpha”值。是否有任何方法可以将集合用作它们包含的合法少数对象

如果有助于了解实际的业务需求

List<BaseWidget> widgets = new List<BaseWidget>();

DerivedWidget specialWidget = new DerivedWidget();
DerivedWidget extraSpecialWidget = new DerivedWidget();

widgets.Add(specialWidget);
widgets.Add(extraSpecialWidget);

specialWidget.Run();
extraSpecialWidget.Run();

if (!widgets.Any(x => x.RunSuccessfully)) return false;
List widgets=newlist();
DerivedWidget specialWidget=新的DerivedWidget();
DerivedWidget extraSpecialWidget=新的DerivedWidget();
widgets.Add(specialWidget);
添加(extraSpecialWidget);
specialWidget.Run();
extraSpecialWidget.Run();
如果(!widgets.Any(x=>x.RunSuccessfully))返回false;
(其中Run()方法设置RunSuccessfully属性,我想在“widgets”列表中反映出来。)

============================================================================

更新


正如回答和评论中所指出的,业务需求模型和试运行示例之间存在一些差异。我将把生活课浓缩成这样:似乎
List
跟踪了它们的更改,而
List
没有。所有对非值类型的引用都将通过引用传递,
List
。然而,字符串是一种值类型,并且总是按值传递。它们也是不可变的,所以每次更改一个字符串时,实际上就是在创建一个新字符串

例如,您可以创建一个包装器类型来包含字符串,并将其存储在
列表中


看起来您的实际业务案例应该能够正常工作,除非它们被声明为结构。

很好。看来你不明白到底发生了什么。这是一篇关于你的好文章

简而言之,在您的示例中,字符串会发生什么情况:

  • 您可以创建一个列表
  • 创建字符串类型的变量initialValue。此变量的值存储在特殊的局部变量容器中。因为字符串是引用类型,所以在局部变量的容器中,它作为指向对象的指针包含
  • 创建新字符串“Alpha”,将其存储在堆中,并将指针(指向此字符串)分配给局部变量
  • 然后将对象添加到列表中。在您的列表中,此对象存储为指向某处的指针
  • 然后,通过将局部变量“initialValue”指定给指向另一个字符串的指针来更改其内容。所以,现在在局部变量'initialValue'中是一个指针,在列表中是另一个指针
  • 那么,解决方案呢

  • 将字符串包装到另一个类中。像这样:

    class Wrapper<T> {
        public T Content {get;set;}
    
    public Wrapper(T content) {
        Content = content;
        }
    }
    
    类包装器{
    公共T内容{get;set;}
    公共包装器(T内容){
    内容=内容;
    }
    }
    
    用法:

    void Main()
    {
        var valuesList = new List<Wrapper<string>>();
        var initialValue = new Wrapper<string>("Alpha");
        valuesList.Add(initialValue);
    
        initialValue.Content = "Bravo";
    
        Console.WriteLine(valuesList[0].Content);
    }
    
    void Main()
    {
    var valuesList=新列表();
    var initialValue=新包装(“Alpha”);
    valuesList.Add(初始值);
    initialValue.Content=“Bravo”;
    Console.WriteLine(valuesList[0].Content);
    }
    
    有点难看的语法

  • 使用clojures:

    void Main()
    {
        List<Func<string>> valuesList = new List<Func<string>>();
        string initialValue = "Alpha";
        valuesList.Add(() => initialValue);
    
        initialValue = "Bravo";
        Console.WriteLine(valuesList[0]() == "Bravo");
    }
    
    void Main()
    {
    列表值列表=新列表();
    字符串initialValue=“Alpha”;
    valuesList.Add(()=>initialValue);
    initialValue=“布拉沃”;
    Console.WriteLine(valuesList[0]()=“Bravo”);
    }
    

  • 在第一个示例中,您没有“更改该对象的属性”,只是更改了一个局部变量以引用不同的对象。您对列表功能的描述(“引用将指向同一对象”)是正确的。第二个示例应该可以正常工作,您尝试过吗?您的代码将引用复制到列表中,然后创建了一个新引用。如果String类有一个假设的“Content”属性,那么initialValue.Content=“Bravo”也会更改列表中的字符串。但它没有,字符串是不可变的。通过将您自己的类的对象放在列表中来解决您的问题,一个具有此类内容属性的类。(facepalm)我刚刚测试了字符串行为的一般原理,但您是对的,对象(类)列表确实可以正常工作。妈的,那太尴尬了。不确定这是否仍然是一个有效的问题。@n请纠正我的错误。由于它们的不变性,它们的行为类似于值类型,所以我假设它们没有查找它。事实证明它们是通过引用传递的,因为它们对于堆栈来说可能太大了。通过引用传递不是您要寻找的术语(我知道OP也这么做了)。通过引用传递将使用
    ref
    out
    ,这确实可以做一些类似于OP所期望的事情(例如
    void Zero(ref int a){a=0;}
    Zero(ref x);
    中会发生变化
    x
    。@delnan我知道“通过引用传递”指的是使用
    ref
    关键字,但我的印象是,它还提到了默认情况下将引用类型传递给方法的方式。还有其他术语吗,或者我应该说相关类型是引用类型吗?这种印象是可以理解的,因为这种滥用术语的现象有点普遍,但它既违背了几十年的CS传统,也有害于消除了一个有用的区别。说该类型是引用类型可能是最好的。其他语言的社区已经为这种类型的参数传递创建了新的术语,但我还没有看到它们用于C#。您可以说引用是通过值(phrasin)传递的