修改公共属性的访问修饰符是破坏性的更改吗?

本文关键字:破坏性 属性 访问 修改 | 更新日期: 2023-09-27 18:18:21

如果我将公共属性的setter的访问修饰符从private更改为public,这会导致引用它的其他程序集中的任何破坏性更改吗?

修改公共属性的访问修饰符是破坏性的更改吗?

UPDATE:这个问题是我2012年1月博客的主题。谢谢你的好问题!


我假设你所说的"中断更改"是指"当我重新编译依赖于这个程序集的代码时,以前可以编译的代码还可以编译吗?"

根据这个定义,严格来说,是的,将过去是私有的属性setter设置为公共是一个突破性的变化。假设你有这样的代码:

// Assembly Alpha
public class Charlie
{
    public int P { get; private set; }
}
public class Delta
{
    public int P { get; set; }
}

然后在引用Alpha的另一个程序集中:

// Assembly Echo
class Foxtrot
{
    static void M(Action<Charlie> f) {}
    static void M(Action<Delta> f) {}
    static void Golf()
    {
        M(y=>{y.P = 123;});
    }
}

编译程序集Echo。类Foxtrot有一个方法Golf,它在M上做重载解析。一个对查理采取行动,一个对德尔塔采取行动。如果lambda参数y的类型是Charlie,则lambda体产生可访问性错误,因此M的重载不是适用的候选。重载解析选择第二个重载,编译成功。

现在你改变装配阿尔法,使查理。P的setter是公共的。重新编译Echo。现在你会得到一个重载解析错误,因为M的两个重载都是同样有效的,没有一个比另一个好。由于Alpha版本的变化,Echo编译失败。你对Alpha的更改是一个突破性的更改。

问题不是"这是一个突破性的变化吗?"它显然是;几乎每一次改变都是突破性的改变。问题应该是,破坏性的变化是否真的会在实践中破坏任何人,如果是这样,与新特性的好处相比,修复这种破坏的成本是多少?

这是一个破坏性的变化,因为它可能导致现有代码不再编译。

某些语言不允许在不重写所有可见访问器的情况下重写属性。VB就是这样一种语言。

假设你有以下c#类:

namespace TestLibrary
    public class A {
        public virtual int X { 
            get { return 0; } 
            private set { Console.WriteLine("A.set_X called."); } 
        }
    }
}

在引用库的VB项目中,有一个类定义:

Class B : Inherits TestLibrary.A
    Public Overrides ReadOnly Property X As Integer
        Get
            Return MyBase.X
        End Get
    End Property
End Class

注意属性上的ReadOnly修饰符。这在VB中是必需的,因为您只定义getter。如果省略它,就会得到编译时错误:

没有'ReadOnly'或'WriteOnly'说明符的

属性必须提供"Get"answers"Set"。

现在,从c#类的setter中删除private修饰符:

namespace TestLibrary
    public class A {
        public virtual int X { 
            get { return 0; } 
            set { Console.WriteLine("A.set_X called."); } 
        }
    }
}

你现在会在你的VB代码中得到一个编译时错误:

'Public Overrides ReadOnly Property X As Integer'不能重写"公共可重写属性X为整数"因为它们的差别是"只读"或"只读"。

引用库的相同代码在您进行更改后不会编译,因此这是一个破坏性更改。

这取决于你对"打破改变"的要求有多严格。

一个定义是:它是否会破坏引用代码的构建或执行。

另一个问题是,其他代码能否确定您做了更改。

根据第一个定义,这不是一个突破性的更改。根据第二种定义,这是一个突破性的变化。

如果其他程序集使用反射来测试setter是否存在,则可能存在。

但是早期绑定的代码不会中断。

另一件要考虑的事情是这是否是语义上的变化。如果这个属性以前只在构建过程中设置,而现在可以随时修改,那么这肯定会破坏那些缓存该值、将其用作Dictionary键等的消费者。

由于该属性的可见性现在增加了,因此静态链接程序集和静态引用不应该有任何问题——这些程序集直到现在才将您的属性视为只读,并且它们将继续这样做。

如果某些程序集使用反射来获取有关属性的信息并执行某些操作,那么这里可能出现的唯一问题是

例如,如果这是业务对象的属性,并且您使用数据绑定在某些控件(如GridViewListView)中显示您的数据(独立于实际框架),则该属性现在可能会被识别为"可更新",并且用户可能会更改到目前为止认为不可变的一些值。