扩展方法未设置值
Extension method not setting value
我有一个产品 class 看起来像这样 -
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
}
我有一个扩展 class 看起来像这样
public static class ProductExtension
{
public static void FixProduct(this Product product)
{
product = new Product(){Name = product.Name.ToUpper()};
//product.Name is now UPPERCASE
}
}
在我的 Main 方法中我有 -
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
。这不会最终影响原始引用 Product
。
修改为下面的方法,在原来传入的对象上设置名称。
public static void FixProduct(this Product product)
{
product.Name = product.Name.ToUpper();
}
我建议进行一些小改动以遵循 fluent interface 模式。而不是 void
,而是 return 新产品。不要使用 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);
}
这种方法的一个明显优势是(如果您认为需要它)您可以支持多个产品 classes,同时保留它们的精确类型,例如:
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,同时保持完全相同的语法。
class DeluxeProduct : Product
{ }
static void Main()
{
var p = new DeluxeProduct
{
Id = 1,
Name = "Steve"
}
.FixProduct();
Console.WriteLine(p.GetType().Name)); //outputs "DeluxeProduct"
}
另一方面,如果您只想 "fix" 产品名称,则可以将其包装在 属性.
中
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
。 this
不会改变这一点。您可以从语法上理解这一点,因为 ref
和 out
需要 variable reference; otherwise only an expression。
很遗憾,您不能将 this
与 ref
或 out
结合使用。
您可以更改任何参数变量的值,但是,除了 ref
或 out
的情况外,最好避免或限制为快速修改传入的值,简化以后的算法代码。
A method is permitted to assign new values to a value parameter. Such
assignments only affect the local storage location represented by the
value parameter—they have no effect on the actual argument given in
the method invocation.
— C# Language Specification
因此,作业确实有效,只是不是以 ref
或 out
的方式。
我有一个产品 class 看起来像这样 -
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
}
我有一个扩展 class 看起来像这样
public static class ProductExtension
{
public static void FixProduct(this Product product)
{
product = new Product(){Name = product.Name.ToUpper()};
//product.Name is now UPPERCASE
}
}
在我的 Main 方法中我有 -
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
。这不会最终影响原始引用 Product
。
修改为下面的方法,在原来传入的对象上设置名称。
public static void FixProduct(this Product product)
{
product.Name = product.Name.ToUpper();
}
我建议进行一些小改动以遵循 fluent interface 模式。而不是 void
,而是 return 新产品。不要使用 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);
}
这种方法的一个明显优势是(如果您认为需要它)您可以支持多个产品 classes,同时保留它们的精确类型,例如:
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,同时保持完全相同的语法。
class DeluxeProduct : Product
{ }
static void Main()
{
var p = new DeluxeProduct
{
Id = 1,
Name = "Steve"
}
.FixProduct();
Console.WriteLine(p.GetType().Name)); //outputs "DeluxeProduct"
}
另一方面,如果您只想 "fix" 产品名称,则可以将其包装在 属性.
中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
。 this
不会改变这一点。您可以从语法上理解这一点,因为 ref
和 out
需要 variable reference; otherwise only an expression。
很遗憾,您不能将 this
与 ref
或 out
结合使用。
您可以更改任何参数变量的值,但是,除了 ref
或 out
的情况外,最好避免或限制为快速修改传入的值,简化以后的算法代码。
A method is permitted to assign new values to a value parameter. Such assignments only affect the local storage location represented by the value parameter—they have no effect on the actual argument given in the method invocation. — C# Language Specification
因此,作业确实有效,只是不是以 ref
或 out
的方式。