我的产品类看起来像这样 -
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
}
我有一个看起来像这样的扩展类
public static class ProductExtension
{
public static void FixProduct(this Product product)
{
product = new Product(){Name = product.Name.ToUpper()};
//product.Name is now UPPERCASE
}
}
在我的主要方法中,我有 -
static void Main(string[] args)
{
Product p = new Product() {ProductId = 1, Name = "steve"};
p.FixProduct();
System.Console.WriteLine(p.Name);
}
这会打印"steve"
,而不是我想要打印的内容:"STEVE"
。
为什么扩展方法中的赋值不起作用?
答案 0 :(得分:6)
扩展方法不能以这种方式使用。在您的方法中,您创建了一个Product
的新实例,然后将其分配给product
,这是对传递的对象的本地引用,而不是原始引用p
。
当您第一次进入该函数时,您所拥有的是引用内存中相同对象的两个引用。
然后在退出方法之前,您有两个对象,一个由每个引用引用,带有product
引用,引用在方法调用结束时由GC清理的局部变量。
解决方案:
要纠正此问题并使其与您尝试的操作最接近,
更改方法以获取ref
参数:
public static void FixProduct(ref Product product)
{
product = new Product() { Name = product.Name.ToUpper() };
//product.Name is now UPPERCASE
}
然后:
ProductExtension.FixProduct(ref p);
我相信一个更好的方法将是(通过拥有它 成员函数或扩展方法)来更新对象 实例化一个新的:
public static void FixProduct(this Product product)
{
product.Name = product.Name.ToUpper();
}
答案 1 :(得分:6)
我建议稍微改变一下fluent interface模式。而不是void
,而是返回新产品。不要使用ref,这很奇怪。
public static class ProductExtension
{
public static Product FixProduct(this Product input)
{
return new Product
{
Name = input.Name.ToUpper(),
Id = input.Id
}
//product.Name is now UPPERCASE
}
}
然后像这样使用它:
static void Main(string[] args)
{
var p = new Product()
{
ProductId = 1,
Name = "steve"
}
.FixProduct();
System.Console.WriteLine(p.Name);
}
这种方法的一个优点是(如果您认为您需要它),您可以支持多个产品类,同时保留其精确类型,例如:
public static class ProductExtension
{
public static T FixProduct<T>(this T input) where T: Product, new
{
return new T
{
Name = input.Name.ToUpper(),
Id = input.Id
}
}
}
现在,您可以在任何派生产品类上使用它,同时保持完全相同的语法。
class DeluxeProduct : Product
{ }
static void Main()
{
var p = new DeluxeProduct
{
Id = 1,
Name = "Steve"
}
.FixProduct();
Console.WriteLine(p.GetType().Name)); //outputs "DeluxeProduct"
}
另一方面,如果你想做的只是“修复”产品的名称,你可以将它包装在一个属性中。
class Product
{
private string _name;
public int Id { get; set; }
public string Name
{
get { return _name; }
set { _name = value.ToUpper(); } //Automatically "fix" it the moment you set it
}
}
...然后根本不需要扩展方法。
答案 2 :(得分:4)
在您的扩展程序中,您要为变量Product
分配新的product
。这最终不会影响原始引用的Product
。
将方法修改为下面的方法,以便在传入对象的原始文件上设置名称。
public static void FixProduct(this Product product)
{
product.Name = product.Name.ToUpper();
}
答案 3 :(得分:1)
参数按值传递,除非它们是ref
或out
。 this
并未改变这一点。您可以从语法上理解这一点,因为ref
和out
需要variable reference;否则只需要expression。
很遗憾,您无法将this
与ref
或out
合并。
你可以更改任何参数变量的值,但ref
或out
除外,最好避免或限制为快速修补传入的值,简化以后的算法代码。
允许方法为值参数指定新值。这样 分配仅影响由...表示的本地存储位置 value参数 - 它们对给定的实际参数没有影响 方法调用。 - C# Language Specification
因此,作业确实有效,而不是ref
或out
方式。