繁体   English   中英

为什么在设置只读的覆盖属性时没有编译器错误?

[英]Why is there no compiler error when setting an overridden property that is read-only?

我一直在学习 C# 继承和接口( 123 )。 在下面的示例中,为什么在派生类中设置属性并通过 auto 属性使属性只读时不会出现编译器错误?

小提琴

using System;
        
public interface IFoo {
    string Title { get; set; }  
}

public abstract class AbstractFoo : IFoo {
    public virtual string Title { get; set; }   
}

public class Foo : AbstractFoo {
    public string Title { get; set; }   
}

public class Bar : AbstractFoo {
    public override string Title { get { return "Read Only Title"; } } 
}

public class Program
{
    public static void Main()
    {
        var foo = new Foo();
        Console.WriteLine(foo.Title);
        
        var bar = new Bar();
        Console.WriteLine(bar.Title); // output: "Read Only Title"
        
        bar.Title = "A new and better title!"; // Why does this not generate an error?
        Console.WriteLine(bar.Title); // output: "Read Only Title" - value was not set
    }
}

潜在的问题是,是否可以防止bar.Titleset ,即仅该派生类的只读字段 我在其他相关答案中看到new关键字可用于重新声明派生类中的属性,但同时建议不要使用它。

您没有覆盖它使其只读。 您只需覆盖get ,并将set作为基本实现。 如果你想让它生气:

public new string Title => "Read Only Title";

现在它知道一个问题。

您似乎不了解接口、实现和重新定义(新)的工作原理。

因此,很快,您无法更改该属性以使其成为只读。

使用“new”关键字,您可以强制 Bar 类型的对象具有只读的 Title 属性。 但是,如果有人将对象强制转换为接口呢? 在这种情况下,Title 属性是接口(读/写)之一,而不是 Bar 类(只读)。

只是为了用一个例子来解释它。 假设您以这种方式更改条形码:

public class Bar : AbstractFoo
{
    public new string Title { get { return "Read Only Title"; } }
}

通过这种方式,您是说 Bar 类具有一个属性,该属性将覆盖 AbstractFoo 类的 Title 属性。

但是,看看这段代码:

        var bar = new Bar();
        bar.Title = "John"; // Compile error: Bar Title is readonly

        var fooInterface = bar as IFoo;
        fooInterface.Title = "Doe"; // OK -> IFoo interface Title is writable
        Console.WriteLine(fooInterface.Title); // Doe -> Title is the one of the interface, not of the Bar class

如你看到的:

  • 如果使用 Bar 类,则不能编写 Title 属性(您想要的)
  • 如果您将对象强制转换为接口,它可以与 AbstractFoo 属性一起使用...我不确定您是否想要

我希望很清楚

.NET 中的属性实际上是get方法和set方法的组合。 尽管这两种方法彼此密切相关,但有时只覆盖一个方法(更典型的是“set”方法)可能是有意义的。 例如,一个基本的“可观察对象”类可能有一个 get 和 set 方法,可以简单地读取或写入一个私有字段,但派生类可能会在不改变 getter 行为的情况下从 setter 中有效地引发事件。

尽管在不更改 setter 的情况下更改 getter 是不寻常的,但在类具有指示其值是否可以通过引用修改、可以保证永远不会更改或两者都不更改的情况下,以及基类不允许修改但不保证不变性。 在这种情况下,默认设置器会抛出NotSupportedException 派生类可能合理地使用专门的 getter,例如懒惰地创建其值,但仍使用默认的 setter。

一切都取决于您需要做什么。

通常人们定义接口来编写一种他们必须遵守的契约。 通过这种方式,您可以编写适用于订阅该合约(接口)的任何类型的对象的代码。

看看你的例子,我不明白你为什么创建一个具有读/写访问权限的接口,然后你要求将它用作只读。

无论如何,您有三个选择:

  • 使用 get/set 方法更改 Bar 类的 Title 属性,但在有人设置值时抛出异常(如 supercat 之前所说)
  • 把Bar类改成我之前写的,但是要注意把对象投到界面上的情况(我之前解释过)
  • 分离只读和可写接口

这里是最后一个选项的代码:

/// <summary>
/// Readeable Foo
/// </summary>
public interface IReadonlyFoo : ICommonFoo
{
    string Title { get; }
}


/// <summary>
/// Readeable and writeable Foo
/// </summary>
public interface IFoo : ICommonFoo
{
    string Title { get; set; }
}


/// <summary>
/// Common interface between readonly and complete Foo
/// </summary>
public interface ICommonFoo
{
    string Name { get; set; }
}



/// <summary>
/// Abstract implementation of common properties/functions
/// </summary>
public abstract class AbstractCommonFoo : ICommonFoo
{
    public string Name { get; set; }
}


/// <summary>
/// Abstract implementation of the writeable Foo
/// </summary>
public abstract class AbstractFoo : AbstractCommonFoo, IFoo
{

    public string Title { get; set; }
}



/// <summary>
/// Abstrac implementation of the readonly Foo
/// </summary>
public abstract class AbstractReadonlyFoo : AbstractCommonFoo, IReadonlyFoo
{
    public string Title { get; }
}


/// <summary>
/// Foo
/// </summary>
public class Foo : AbstractFoo
{

}


/// <summary>
/// Readonly Foo (Bar)
/// </summary>
public class Bar : AbstractReadonlyFoo
{

}

如果你真的需要同时拥有只读和可写两种情况,也许这对你来说是最好的。 所以在你的代码中,任何方法都会接受:

  • ICommonFoo 当不需要 Title 时(因此您可以同时传递 Foo 和 Bar 类)
  • 仅读取 Title 时的 IReadonlyFoo(您可以同时传递 Foo 和 Bar 类)
  • IFoo 当您需要设置标题时(您只能传递 Foo)

使用这两个接口会使您的代码更长,但更易于使用。 例如,在需要设置 Title 的方法中,您将参数放入 IFoo 接口,因此如果有人尝试传递 Bar,编译器会显示错误。 如果您使用“throw NotImplementedException”(其他解决方案之一),您的代码将编译,但在运行时会失败。

这不是一个简单的话题。 我希望我以一种好的方式解释它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM