扩展方法没有设置值

时间:2017-10-14 19:09:07

标签: 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 = 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"。 为什么扩展方法中的赋值不起作用?

4 个答案:

答案 0 :(得分:6)

扩展方法不能以这种方式使用。在您的方法中,您创建了一个Product的新实例,然后将其分配给product,这是对传递的对象的本地引用,而不是原始引用p

当您第一次进入该函数时,您所拥有的是引用内存中相同对象的两个引用。

enter image description here

然后在退出方法之前,您有两个对象,一个由每个引用引用,带有product引用,引用在方法调用结束时由GC清理的局部变量。

enter image description here

解决方案:

  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();
    }
    

答案 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)

参数按值传递,除非它们是refoutthis并未改变这一点。您可以从语法上理解这一点,因为refout需要variable reference;否则只需要expression

很遗憾,您无法将thisrefout合并。

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

  

允许方法为值参数指定新值。这样   分配仅影响由...表示的本地存储位置   value参数 - 它们对给定的实际参数没有影响   方法调用。      - C# Language Specification

因此,作业确实有效,而不是refout方式。