C# 扩展方法没有设置值
我有一个产品类,看起来像这样-C# 扩展方法没有设置值,c#,extension-methods,C#,Extension Methods,我有一个产品类,看起来像这样- 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 = n
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”
。
为什么扩展方法中的赋值不起作用?扩展方法不能这样使用。在方法中,创建
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();
}
在扩展方法中,您将为变量
Product
分配一个新的Product
。这不会影响原始引用的产品
将该方法修改为下面的方法,以设置原始传入对象的名称
public static void FixProduct(this Product product)
{
product.Name = product.Name.ToUpper();
}
我建议按照一个模式做一个小小的改变。将新产品退回,而不是
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 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
}
}
…然后您根本不需要扩展方法。参数按值传递,除非它们是
ref
或out
<代码>此不会改变这一点。您可以从语法上理解这一点,因为ref
和out
需要;否则,只需要一个
不幸的是,您不能将此
与参考
或输出
组合
您可以更改任何参数变量的值,但是,除了ref
或out
的情况外,最好避免或限制对传入值进行快速润色,以简化以后的算法代码
允许使用方法为值参数指定新值。这样的
分配仅影响由表示的本地存储位置
值参数它们对中给定的实际参数没有影响
方法调用。
-
因此,分配确实有效,只是不是以
ref
或out
的方式。在FixProduct
中创建Product
的新实例。它将被GC删除,不会影响原始对象product=new product()
重定向变量以引用新对象,而不是作为参数传入的对象(而且它也应该是不必要的,为什么不干脆product.Name=
)您需要将此产品
作为ref
参数传递,以便在扩展方法之外反映其值的更改。然而,扩展方法。您可以使用诸如var p=new Product{ProductId=1,Name=“steve”}.FixProduct()之类的函数
为什么否决?一个更优雅的方法是将它放在原始的产品
类中(如果可能的话),例如Product.Fix(prod)代码>@IanH.-我同意,或者仍然使用扩展方法,但只是更新对象而不是创建新的one@Bryan-不客气:)我建议使用我听过的第二种解决方案。我想在我写答案时,你在编辑你的帖子。我也这么做了,但是让一个扩展方法返回它正在扩展的类型感觉有点奇怪。不,这很常见。事实上,所有LINQ扩展方法都使用此模式,以及遵循生成器模式的对象,例如大多数IoC容器。让人感觉产品是不可变的,就像对字符串执行操作一样。我必须一直和LINQ在一起,我已经习惯了,我想这只是一个例子,还有更多的事情要做。但是如果你只是想强制名字大写,也许可以用一个属性来包装——我会在最后编辑我的答案来提出这个建议。这是对我真正做的事情的一个粗略简化,但它已经验证了我最初尝试的是不可行的。谢谢你的建议。
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
}
}