修改公共财产的访问修饰符是一个突破性的变化吗?

时间:2011-12-20 19:04:27

标签: c#

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

5 个答案:

答案 0 :(得分:34)

更新This question was the topic of my blog in January 2012。谢谢你提出的好问题!


我认为通过“破坏变化”你的意思是“当我重新编译依赖于这个程序集的代码时,用于编译的代码是否仍然编译?”

根据这个定义,严格地说,是的,将过去私有的属性设置者公开是一个突破性的变化。假设你有这段代码:

// 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进行超载分辨.M有两个重载;一个对查理采取行动,另一个采取行动对达美。如果lambda参数y的类型为Charlie,那么lambda主体会产生可访问性错误,因此M的重载不是适用的候选者。重载决策选择第二次重载并编译成功。

现在你更改程序集Alpha,以便Charlie.P的setter是公共的。你重新编译Echo。现在你得到一个重载分辨率错误,因为M的两个重载同样有效,并且两者都不比另一个好。由于Alpha的变化,Echo的编译失败。你对Alpha的改变是一个重大变化。

问题不在于“这是一个突破性变化吗?”显然是;几乎所有的变化都是某种突破性变化。问题应该是突破性变化是否真的会在实践中打破任何人,如果是这样,与新功能的好处相比,修复中断的成本是多少?

答案 1 :(得分:18)

这是一个重大变化,因为它可能导致现有代码不再编译。

某些语言不允许您覆盖属性而不覆盖所有可见的访问者。 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'说明符的属性必须提供   既有'获得'又有'集'。

现在,从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'无法覆盖   'Public Overridable Property X As Integer'因为它们不同   'ReadOnly'或'WriteOnly'。

在您进行更改后,引用您的库的相同代码无法编译,因此这是一个重大变化。

答案 2 :(得分:5)

这取决于你想要对“破坏变化”的严格程度。

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

另一个是,其他代码可以确定您进行了更改。

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

答案 3 :(得分:4)

如果那些其他程序集使用反射来测试setter的存在,则可能是这样。

但早期绑定的代码不会破坏。

要考虑的另一件事是这是否是语义上的变化。如果该属性以前只在构造期间设置,现在可以随时修改,那肯定会破坏缓存该值的消费者,将其用作词典键等。

答案 4 :(得分:3)

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

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

例如,如果这是业务对象的属性,则使用数据绑定在某些控件中显示数据,例如GridView或{ {1}}(独立于实际框架),现在可能会将此属性识别为“可更新”,并且用户可能会更改您(以及其他一些程序集)到目前为止认为不可变的某些值。