C# 在扩展方法中更改数组大小无效?

C# 在扩展方法中更改数组大小无效?,c#,arrays,unity3d,extension-methods,concat,C#,Arrays,Unity3d,Extension Methods,Concat,所以基本上我为数组类型编写了一个小小的Add扩展方法 using System; using System.Linq; public static class Extensions { public static void Add<T>(this T[] _self, T item) { _self = _self.Concat(new T[] { item }).ToArray(); } } public class Program {

所以基本上我为数组类型编写了一个小小的Add扩展方法

using System;
using System.Linq;
public static class Extensions 
{
    public static void Add<T>(this T[] _self, T item)
    {
        _self = _self.Concat(new T[] { item }).ToArray();
    }
}
public class Program
{
    public static void Main()
    {
        string[] test = { "Hello" };
        test = test.Concat(new string[] { "cruel" }).ToArray();
        test.Add("but funny");
        Console.WriteLine(String.Join(" ", test) + " world");
    }
}
我在这里出错了什么,我怎么能把它用作扩展

.dotnetfiddle:或者


如果能找到一种方法让我可以继续进行通话测试,那就太好了

您正在为参数指定一个新引用,除非您将其作为ref参数传递,否则它不会更改实际数组。因为这是一种扩展方法,所以它不是一个选项。因此,考虑使用常规方法:

public static void Add<T>(ref T[] _self, T item)
{
    _self = _self.Concat(new T[] { item }).ToArray();
}

Add(ref test, "but funny");
现在,如果为arr2分配一个新引用,它将不会更改arr1的引用。Array.Resize所做的是,由于无法调整数组的大小,它创建一个新数组,将所有元素复制到一个新数组,并将该新引用指定给参数_self。因此,它更改了_self指向的位置,但由于_self和test是两个不同的引用,如arr1和arr2,更改其中一个不会影响另一个

另一方面,如果像我的第一个示例中那样将array作为ref传递给方法,则array.Resize也将按预期工作,因为在这种情况下,将不会复制引用:

public static void Add<T>(ref T[] _self, T item)
{
    Array.Resize(ref _self, _self.Length + 1);
    _self[_self.Length - 1] = item;
}
我相信_self=会将_self对象的副本创建到为参数_self创建的局部变量中,因此不会更新原始变量。使用类似列表的引用类型,或者创建一个返回新数组的静态方法


就原因的“感觉”而言:您正在对一个变量实例调用一个方法-您不能从在其上下文中执行的代码中更改该实例

您可以这样更改代码:

string[] arr1 = { "Hello" };
string[] arr2 = arr1;
public static class Extensions 
{
    public static T[] Add<T>(this T[] _self, T item)
    {
        return _self.Concat(new T[] { item }).ToArray();
    }
}
public class Program
{
    public static void Main()
    {
        string[] test = { "Hello" };
        test = test.Concat(new string[] { "cruel" }).ToArray();
        test = test.Add("but funny");
        Console.WriteLine(String.Join(" ", test) + " world");
    }
}

作为旁注-用法与Concat方法相同。

不能使用扩展方法,因为this和ref-kewords不能一起使用。您可以返回新数组作为结果并进一步使用它。您应该真正使用列表,如果您需要数组,只需执行.ToArray即可。您正在尝试执行List已经在执行的操作。@Franck我正在使用Unity3D,编辑器处理数组的方式更好,实际上它根本不会处理List,出于性能原因,它们应该保留数组,因为这样的扩展方法只会在编辑器中调用,不是游戏本身。@modiX在这种情况下,您应该在问题中添加Unity标记。根据Microsoft源代码,List实际上使用数组来存储数据,所以List与string[]是一样的。不同之处在于少数额外房产的分配规模。访问数据的速度相同,添加由Microsoft自己优化。我更新了我的问题,因为使用Array.Resize的另一种方法也不起作用。我不明白为什么对self的引用是这样的。使用ref关键字应该没有必要,因为它已经是通过引用调用的了。如果能找到一个解决方案,我就可以使用扩展方法,比如列表上的Add方法。即test.Additem;。有什么想法吗?@modiX我补充解释了为什么它不起作用。如果有任何不清楚的地方,请告诉我谢谢,因此当它更改时,它只会更改Add extension方法范围内的变量。这假设,_self作为值传递,而不是作为引用传递。但这就是重点。默认情况下应通过引用调用数组,为什么它们不进入扩展方法?@modiX您应该知道,引用类型变量不包含实际值,它们在内存中包含一个可以找到实际对象的地址。一旦理解了传递引用类型参数的含义,它的引用就会复制到另一个变量。就像我的arr1和arr2示例一样。因此,如果我更改arr2指向的位置,它不会影响arr1,因为它们是完全不同的变量。modiX-假设您不是在编写扩展方法,而是在数组类型上编写实际的方法。你所做的就是说这个=。。。。这是无效的:您需要返回新实例,或者修改自己。由于数组是不可变的,您不能修改自己。对不起,是的,我也不知道我的意思。关键是您正在覆盖局部变量_self,而不是更新调用methodAh的源表达式-我的意思是不可变的。Resize不会修改数组,它会返回一个新的调整大小的数组,这是不好的,因为它违反了框架中类似类的预期行为,Add修改了对象本身。很容易有人错误地使用这种方法。至少给这个方法起一个不同的名字,比如Concat,它表明这个对象本身没有被修改。@NineBerry OP决定这样命名它。
public static void Add<T>(ref T[] _self, T item)
{
    Array.Resize(ref _self, _self.Length + 1);
    _self[_self.Length - 1] = item;
}
public static class Extensions 
{
    public static T[] Add<T>(this T[] _self, T item)
    {
        return _self.Concat(new T[] { item }).ToArray();
    }
}
public class Program
{
    public static void Main()
    {
        string[] test = { "Hello" };
        test = test.Concat(new string[] { "cruel" }).ToArray();
        test = test.Add("but funny");
        Console.WriteLine(String.Join(" ", test) + " world");
    }
}