扩展方法未设置值

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 清理的局部变量。

解决方案:

  1. 要更正此问题并使其最接近您尝试执行的操作, 更改您的方法以获取 ref 参数:

    public static void FixProduct(ref Product product)
    {
        product = new Product() { Name = product.Name.ToUpper() };
        //product.Name is now UPPERCASE
    }
    

    然后:

    ProductExtension.FixProduct(ref p);
    
  2. 我相信一个更好的方法是(通过 成员函数或扩展方法)来更新对象 实例化一个新的:

    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
    }
}

...然后您根本不需要扩展方法。

参数按值传递,除非它们是 refoutthis 不会改变这一点。您可以从语法上理解这一点,因为 refout 需要 variable reference; otherwise only an expression

很遗憾,您不能将 thisrefout 结合使用。

可以更改任何参数变量的值,但是,除了 refout 的情况外,最好避免或限制为快速修改传入的值,简化以后的算法代码。

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

因此,作业确实有效,只是不是以 refout 的方式。