C#扩展方法是否影响当前实例或返回相同类型的新对象

本文关键字:同类型 新对象 对象 返回 是否 方法 扩展 影响 实例 | 更新日期: 2023-09-27 17:57:55

使用扩展方法扩展非静态类型时,对"this"执行的工作是发生在扩展类的原始实例上,还是返回应用了这些更改的新实例?或者它可以做任何一种,这取决于扩展方法是如何编写的?

考虑以下示例扩展方法:

public static AddressEntity FromAddressLight(this AddressEntity addressEntity, AddressLight addressLight)
{
    if (addressLight == null)
    {
        return null;
    }
    var result = new AddressEntity
    {
        Id = addressLight.AddressId,
        AddressName = addressLight.AddressName,
        // other mapping conversions here
    };
    return result;
}

以下哪种调用方式有效?

AddressLight light = new AddressLight(); // assume constructor populates this
AddressEntity myEntity = new AddressEntity(); // this constructor does not populate anything
myEntity.FromAddressLight(light); // Will this cause myEntity take on the value of "result" in the extension method?...
myEntity = myEntity.FromAddressLight(light); // ... or is this needed to copy "result" into myEntity?
// ... and why is this not something Visual Studio allows? Does this not become a static member of the extended class?
myEntity = AddressEntity.FromAddressLight(light);

C#扩展方法是否影响当前实例或返回相同类型的新对象

快速回答。。。这取决于。

您给出的示例返回了AddressEntity的一个新实例,但它没有理由不能返回相同的实例,这对于链接或完全不同的东西来说很方便。

这取决于你。

myEntity = myEntity.FromAddressLight(light);是在您的示例中正确使用此方法的方法。

myEntity = AddressEntity.FromAddressLight(light);不起作用,因为您调用的是静态方法而不是实例方法,尽管考虑到静态声明,这有点违背直觉。

这是一种奇怪的扩展方法(作为"正常"静态方法更有意义),但这里是,按照行的顺序(跳过前两行):

  1. 这不会有任何作用。扩展方法返回一个新对象,该对象没有分配给任何对象,因此超出了范围并被垃圾回收。

  2. 这将完全按照你的想法进行,并且是使用这种方法的唯一"合理"方式。

  3. 这是不允许的,因为没有"AddressEntity"的实例传递给函数。不是显式的(显然您只传递了一个参数)或隐式的(因为您使用的是类型来访问它,而不是对象)。

特别是因为您正在尝试执行(3),所以我不会将此函数作为扩展方法(它甚至不使用任何AddressEntity字段,所以它为什么要采用这种类型?),而是将其作为一个普通的静态方法。

明确回答您的问题(在标题中):

扩展方法没有什么特别的,它只是有一个隐式参数("this"参数),所以调用它时看起来很好。如果它返回一个新值,你就会得到一个新的值。如果修改"this"参数,它将修改现有值。事实上,两者都可以毫无问题地完成。它完全取决于扩展方法的内容。

作为一个扩展方法,这通常很奇怪。考虑一下,扩展方法只是语法糖(正如前面其他人提到的那样),普通用户实际上会期望发生以下两件事之一:

  1. 我调用了扩展方法,并得到了我自己的修改版本
  2. 我调用扩展方法并返回一个转换后的类型

我认为第二个是你真正想要的:

public static AddressEntity ToAddressEntity(this AddressLight obj)
{
    return new AddressEntity
    {
        Id = obj.AddressId,
        AddressName = obj.AddressName,
        // other mapping conversions here
    };
}

请记住,扩展方法不是基于返回类型,而是基于this之后指定的类型。所以在这种情况下,你可以只做AddressEntity addEntity = SomeAddressLight.ToAddressEntity();。这也是你在任何BCL中都看不到模式FromSomething()的原因,因为在扩展方法的情况下,这感觉非常违反直觉。