扩展方法和本地 'this' 变量
Extension method and local 'this' variable
据我所知,扩展方法中的 this
作为 ref
变量传递。我可以通过
来验证这一点
public static void Method<T>(this List<T> list)
{
list.Add(default(T));
}
List<int> ints = new List<int>(new int[] { 1, 2, 3, 4, 5 });
ints.Method();
我的 List<int> ints
现在是 1, 2, 3, 4, 5, 0
。
但是当我这样做时
public static void Method<T>(this List<T> list, Func<T, bool> predicate)
{
list = list.Where(predicate).ToList();
}
List<int> ints = new List<int>(new int[] { 1, 2, 3, 4, 5 });
ints.Method(i => i > 2);
我希望我的 List<int> ints
是 3, 4, 5
,但保持不变。我是否漏掉了一些明显的东西?
this
扩展方法参数按值传递,而不是按引用传递。这意味着在进入扩展方法时,您有两个变量指向相同的内存地址:原始 ints
和 list
参数。当您在扩展方法中向列表中添加一个项目时,它会反映在 ints
中,因为您修改了一个由两个变量引用的对象。当您重新分配 list
时,将在托管堆上创建一个新列表,并且扩展方法的参数指向该列表。 ints
变量仍然指向旧列表。
好吧,当您尝试修改某些 class 实例的 属性 时,您甚至不需要 ref
因为您正在修改实例而不是引用它。
在此示例中,您不需要 ref
关键字,因为您修改 属性 :
class MyClass
{
public int MyProperty { get; set; }
}
static void Method(MyClass instance)
{
instance.MyProperty = 10;
}
static void Main(string[] args)
{
MyClass instance = new MyClass();
Method(instance);
Console.WriteLine(instance.MyProperty);
}
输出:10
这里你确实需要 ref
关键字,因为你使用的是引用而不是实例:
...
static void Method(MyClass instance)
{
// instance variable holds reference to same object but it is different variable
instance = new MyClass() { MyProperty = 10 };
}
static void Main(string[] args)
{
MyClass instance = new MyClass();
Method(instance);
Console.WriteLine(instance.MyProperty);
}
输出:0
对于您的场景来说是相同的,扩展方法与普通静态方法相同,如果您在方法内部创建新对象,那么您可以使用 ref
关键字(虽然扩展方法不可能)或 return 这个对象,否则对它的引用将会丢失。
所以在你的第二种情况下你应该使用:
public static List<T> Method<T>(this List<T> list, Func<T, bool> predicate)
{
return list.Where(predicate).ToList();
}
List<int> ints = new List<int>(new int[] { 1, 2, 3, 4, 5 });
ints = ints.Method(i => i > 2);
foreach(int item in ints) Console.Write(item + " ");
输出:3, 4, 5
据我所知,扩展方法中的 this
作为 ref
变量传递。我可以通过
public static void Method<T>(this List<T> list)
{
list.Add(default(T));
}
List<int> ints = new List<int>(new int[] { 1, 2, 3, 4, 5 });
ints.Method();
我的 List<int> ints
现在是 1, 2, 3, 4, 5, 0
。
但是当我这样做时
public static void Method<T>(this List<T> list, Func<T, bool> predicate)
{
list = list.Where(predicate).ToList();
}
List<int> ints = new List<int>(new int[] { 1, 2, 3, 4, 5 });
ints.Method(i => i > 2);
我希望我的 List<int> ints
是 3, 4, 5
,但保持不变。我是否漏掉了一些明显的东西?
this
扩展方法参数按值传递,而不是按引用传递。这意味着在进入扩展方法时,您有两个变量指向相同的内存地址:原始 ints
和 list
参数。当您在扩展方法中向列表中添加一个项目时,它会反映在 ints
中,因为您修改了一个由两个变量引用的对象。当您重新分配 list
时,将在托管堆上创建一个新列表,并且扩展方法的参数指向该列表。 ints
变量仍然指向旧列表。
好吧,当您尝试修改某些 class 实例的 属性 时,您甚至不需要 ref
因为您正在修改实例而不是引用它。
在此示例中,您不需要 ref
关键字,因为您修改 属性 :
class MyClass
{
public int MyProperty { get; set; }
}
static void Method(MyClass instance)
{
instance.MyProperty = 10;
}
static void Main(string[] args)
{
MyClass instance = new MyClass();
Method(instance);
Console.WriteLine(instance.MyProperty);
}
输出:10
这里你确实需要 ref
关键字,因为你使用的是引用而不是实例:
...
static void Method(MyClass instance)
{
// instance variable holds reference to same object but it is different variable
instance = new MyClass() { MyProperty = 10 };
}
static void Main(string[] args)
{
MyClass instance = new MyClass();
Method(instance);
Console.WriteLine(instance.MyProperty);
}
输出:0
对于您的场景来说是相同的,扩展方法与普通静态方法相同,如果您在方法内部创建新对象,那么您可以使用 ref
关键字(虽然扩展方法不可能)或 return 这个对象,否则对它的引用将会丢失。
所以在你的第二种情况下你应该使用:
public static List<T> Method<T>(this List<T> list, Func<T, bool> predicate)
{
return list.Where(predicate).ToList();
}
List<int> ints = new List<int>(new int[] { 1, 2, 3, 4, 5 });
ints = ints.Method(i => i > 2);
foreach(int item in ints) Console.Write(item + " ");
输出:3, 4, 5