2017-02-06 101 views
1

我有一种情况,我想让普通属性在具有默认值的派生类中只读。 我使用关键字以下列方式目的:将BaseClass属性更改为DerivedClass中的只读属性并覆盖

public abstract class BaseClass 
{ 
    public virtual string SomeInfo { get; set; } 
} 
public class DerivedClass1 : BaseClass 
{ 
    public new string SomeInfo => "ChildInfo1"; // C# 6.0 equivalent of { get { return "ChildInfo1"; } } 
} 

它工作正常,并new DerivedClass1().SomeInfo不能被分配到 - 它是只读的。我知道,一个可以通过基类访问:

BaseClass b1 = new DerivedClass1(); 
b1.SomeInfo = "ChildInfo1 changed"; 

我只想无法用户意外更改,并通过基类,那将是目的,在这种情况下,它acceptible。

但是,如果派生类会是这样的:

public class DerivedClass2 : BaseClass 
{ 
    public override string SomeInfo => "ChildInfo2"; 
} 

那么这个属性将可以使用,您可以更改貌似,但它不会改变的,我想知道为什么?

var d2 = new DerivedClass2(); 
d2.SomeInfo = "ChildInfo2 changed"; 
Console.WriteLine(d2.SomeInfo); // output: ChildInfo2 

UPDATE:
我已经添加了新的答案作为第三个选择,可能是最好的。

+1

*“我只想无法用户意外更改,并通过基类,那将是目的,在这种情况下,它acceptible” *如果程序员调用一个接受'BaseClass'参数的方法,程序员不知道该方法改变'SomeInfo'吗?无论如何,你提出的建议违反了[Liskov替代原则](https://en.wikipedia.org/wiki/Liskov_substitution_principle),应该被怀疑。 –

+0

我在想这可能违反了LSP。 有没有其他(更好)的方式来实现这一点,而不违反它? – borisdj

+0

那么,你可以创建一个只读的接口并传递它,但是当然这需要修改现有的方法,所以它可能不是一个选项。 –

回答

0

你应该避免在这种情况下使用“新”。 定义在派生类中,而不是在这样一个只读属性:

public override string SomeInfo { get { return "ChildInfo"; } } 

,您仍然可以设置您的属性,但它始终会返回默认值“儿童信息”。

+0

不一样吗? 这给出警告,它隐藏了继承的成员,并建议添加新的,或覆盖,如果这是有意的。 添加新只是明确确认隐藏但行为是一样的吗? 因此LSP仍然受到侵犯。 – borisdj

+0

@Boris,你是对的。我忘了写“覆盖”。现在修复。是的,在这种情况下,使用“新”或“覆盖”得到相同的结果。但是,如您所知,使用“新”会违反LSP。 – jacktric

+0

“新”和“覆盖”是不一样的。 您的初始文章没有这两个关键字,隐含意味着隐藏,所以“新”与无。 “覆盖”,因为你现在已经更新,就像我的第二个选项,在这种情况下,你可以访问setter,但getter忽略它,所以这是违反直觉的。你认为你已经改变了财产,但你没有。 这就是为什么第三种选择,我补充说作为另一个答案,是最好的。 – borisdj

1

在你的基类,你有

public virtual string SomeInfo { get; set; } 

它是一个不错的定义:如果你有一个Expression健全属性来覆盖它,你与

覆盖,获取属性

private string _someInfo; 
public string SomeInfo 
{ 
    get {return _someInfo;} 
    set {_someInfo = value;} 
} 

public string SomeInfo 
{ 
    get {return "ChildInfo2";} 
} 

但是你不重写set属性,所以你仍然可以设置私有变量,但它不会改变其他任何东西。

如果你看看你的第一个例子:

BaseClass b1 = new DerivedClass1(); 
b1.SomeInfo = "ChildInfo1 changed"; 

碰巧确切同样的事情,你可以设置属性,因为基类确实有setter和设置为私有变量,但如果你试图产值SomeProperty,你看,它没有改变,仍然是“ChildInfo1”

+0

所以setter在它后面设置私有字段,但getter返回的是get方法的一部分,它实际上忽略了这个字段。 我现在明白了。 – borisdj

+0

@Boris是的,这个表达式的属性只是意味着:“总是返回那个常量值” –

0

我现在做了第三个选项,可能是最好的,在构造函数中设置默认值。 感谢@MatthewWatson提请注意LSP和@MaksimSimkin的解释。

public abstract class BaseClass 
{ 
    public string SomeInfo { get; set; } 
} 

public class DerivedClass1 : BaseClass 
{ 
    public DerivedClass1() 
    { 
     base.SomeInfo = "ChildInfo1"; 
    } 
    public new string SomeInfo => base.SomeInfo; 
} 

这使得SomeInfo readOnly的从DerivedClass访问时,如果通过BaseClass的访问是编辑,并且定期更换适用,并且是来自两类可见。所以没有'未知'的行为。

PS对于旧版本的C#这相当于:

public abstract class BaseClass 
{ 
    protected string _someInfo; 
    public string SomeInfo 
    { 
     get { return _someInfo; } 
     set { _someInfo = value; } 
    } 
} 

public class DerivedClass1 : BaseClass 
{ 
    public DerivedClass1() 
    { 
     _someInfo = "ChildInfo1"; 
    } 
    public new string SomeInfo 
    { 
     get { return _someInfo; } 
    } 
}