Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 这是否违反了Liskov替换原则,如果是,我该怎么办?_C#_Wpf_Mvvm_Liskov Substitution Principle - Fatal编程技术网

C# 这是否违反了Liskov替换原则,如果是,我该怎么办?

C# 这是否违反了Liskov替换原则,如果是,我该怎么办?,c#,wpf,mvvm,liskov-substitution-principle,C#,Wpf,Mvvm,Liskov Substitution Principle,用例:我正在使用数据模板将视图与ViewModel匹配。数据模板通过检查所提供的具体类型中最派生的类型来工作,它们不查看它提供的接口,所以我必须在没有接口的情况下完成这项工作 我在这里简化了这个示例,省略了NotifyPropertyChanged等,但在现实世界中,视图将绑定到文本属性。为简单起见,假设具有TextBlock的视图将绑定到ReadOnlyText,而具有TextBox的视图将绑定到WritableText class ReadOnlyText { private str

用例:我正在使用数据模板将视图与ViewModel匹配。数据模板通过检查所提供的具体类型中最派生的类型来工作,它们不查看它提供的接口,所以我必须在没有接口的情况下完成这项工作

我在这里简化了这个示例,省略了NotifyPropertyChanged等,但在现实世界中,视图将绑定到文本属性。为简单起见,假设具有TextBlock的视图将绑定到ReadOnlyText,而具有TextBox的视图将绑定到WritableText

class ReadOnlyText
{
    private string text = string.Empty;

    public string Text
    {
        get { return text; }
        set
        {
            OnTextSet(value);
        }
    }

    protected virtual void OnTextSet(string value)
    {
        throw new InvalidOperationException("Text is readonly.");
    }

    protected void SetText(string value)
    {
        text = value;
        // in reality we'd NotifyPropertyChanged in here
    }
}

class WritableText : ReadOnlyText
{
    protected override void OnTextSet(string value)
    {
        // call out to business logic here, validation, etc.
        SetText(value);
    }
}

通过重写OnTextSet并更改其行为,我是否违反了?如果是这样的话,还有什么更好的方法呢?

只有在
ReadOnlyText.OnTextSet()
的规范承诺抛出时

想象一下这样的代码

public void F(ReadOnlyText t, string value)
{
    t.OnTextSet(value);
}
如果这个没有扔出去,对你有意义吗?如果不是,那么WritableText是不可替代的


在我看来,
可写文本
应该继承自文本。如果在
ReadOnlyText
WritableText
之间有一些共享代码,请将其放入Text或它们都从中继承的另一个类中(从
Text
继承的类)

我认为这取决于合同

如果ReadOnlyText的合同中说“任何设置文本的尝试都会引发异常”,那么您肯定违反了LSP

否则,代码中仍然有一个尴尬之处:只读文本的setter

在特定情况下,这是可接受的“非规范化”。我还没有找到一种不依赖大量代码的更好方法。在大多数情况下,干净的界面是:

IThingieReader
{
    string Text { get; }
    string Subtext { get; }
    // ...
}

IThingieWriter
{
    string Text { get; set; }
    string Subtext { get; set; }
    // ...
}

…并仅在适当时实施接口。但是,如果您必须处理例如
文本
可写而
子文本
不可写的实例,那么这种情况就会发生,并且对于许多对象/属性来说,这是一件很痛苦的事情。

是的,如果受保护的覆盖void OnTextSet(字符串值)也引发了类型为“InvalidOperationException”的异常,则不会发生这种情况或者从中继承


您应该有一个基类Text,并且从中继承ReadOnlyText和WritableText。

LSP声明子类应该是其超类的可替代类(请参见stackoverflow问题)。要问自己的问题是,“可写文本是一种只读文本吗?”答案显然是“不”,事实上,它们是相互排斥的。所以,是的,这个代码违反了LSP。但是,可写文本是否为可读文本(而非只读文本)?答案是肯定的。因此,我认为答案是看一下您在每种情况下试图做什么,并可能对抽象进行如下更改:

class ReadableText
{
    private string text = string.Empty;
    public ReadableText(string value)
    {
        text = value;
    }

    public string Text
    {
        get { return text; }
    }
}          

class WriteableText : ReadableText
{
    public WriteableText(string value):base(value)
    {

    }

    public new string Text
    {
        set
        {
            OnTextSet(value);
        }
        get
        {
            return base.Text;
        }
    }
    public void SetText(string value)
    {
        Text = value;
        // in reality we'd NotifyPropertyChanged in here       
    }
    public void OnTextSet(string value)
    {
        // call out to business logic here, validation, etc.       
        SetText(value);
    }
}     
需要明确的是,我们在可写类的Text属性上使用new关键字对可读类隐藏Text属性。
发件人:
使用new关键字时,将调用新的类成员,而不是已替换的基类成员。这些基类成员称为隐藏成员。如果派生类的实例被强制转换为基类的实例,则仍然可以调用隐藏的类成员

(对于那些还没有喝咖啡的人)@SomeMiscGuy:对不起,添加了链接:)顺便说一句,可以使用DataTemplateSelector基于实现接口的类解析数据模板。这一个对我来说很好:正如我所说的,这将是理想的,但我不能使用接口,因为数据模板不会关闭接口,它们需要一个具体的类型。这不太适合编译,但可以修改以工作。我不知道可以在派生类中为基类中具有getter的属性定义属性setter。我学到了一些新东西。这会奏效的。谢谢有趣的是,我需要将派生类中的Text属性声明为“new”以避免编译器警告,但在我看来,这并不是真正的新属性,因为我没有重写getter。我不相信您希望SetText和OnTextSet被公开。我编辑了文章以包含new关键字。C#告诉我们,我们将文本属性隐藏在Readable类中,因为该属性仅为Readable(getter),但在Writeable类中,该属性为Readable(getter)和Writeable(setter)。@Steve Ellinger:True,在我的实现中,基类负责更新属性,并在业务逻辑更改值时通知视图。根据我的具体问题,您是正确的,但Brandon指出了我对属性设置器和获取器的误解,这使我能够更优雅地解决问题。谢谢你的信息。