实现接口扩展属性(继承?)

时间:2017-08-03 15:27:43

标签: c# inheritance interface

我有一个必须解决的接口实现: 这是一个例子:

interface ISettingsBase
{
    string Name { get; set;}
    DateTime TimeStamp { get; set; }
}

public class SettingsBase : ISettingsBase
{
    public string Name { get; set; }
    public DateTime TimeStamp { get; set; }
}

interface IWorkerBase
{
    ISettingsBase Settings { get; set; }
}

public class WorkerBase: ISettingsBase
{
    public ISettingsBase Settings { get; set; }
}

interface IExtendedSettings : ISettingsBase
{
    string FilePath { get; set; }
}

interface IWorkerExtended : IWorkerBase
{
    // This configuration property should respect those of
    // the IWorkerBase and increase the features.
    IExtendedSettings Settings { get; set; }
}

public class WorkerExtended : WorkerBase, IWorkerExtended
{
    ...
    ...
    public IExtendedSettings Settings { get; set; }
}

问题是编译器告诉我WorkerExtended中存在错误,并且我不尊重IWorkerBase.Settings接口的实现。问题是我需要新的改进类也支持具有更多属性的配置。

2 个答案:

答案 0 :(得分:4)

interface IWorkerBase
{
    ISettingsBase Settings { get; set; }
}

interface IWorkerExtended : IWorkerBase
{
    IExtendedSettings Settings { get; set; }
}

这已经存在问题,因为IWorkerExtended.Settings隐藏 Settings IWorkerBase成员所需的成员。因此,IWorkerExtended的实施者仍需提供原始Settings成员(类型为ISettingsBase)才能过渡实施IWorkerBase

编译器会在这里警告你,因为这个成员隐藏在这里。通常这样做会是一个错误,因此您需要使用new键盘表示您愿意这样做:

interface IWorkerExtended : IWorkerBase
{
    new IExtendedSettings Settings { get; set; }
}

请注意,这对实施者没有影响。因此,在实现该接口时,您必须提供两个Settings成员。您可以通过显式实现基接口来实现:

public class Worker : IWorkerExtended
{
    public IExtendedSettings Settings { get; set; }

    ISettingsBase IWorkerBase.Settings { get; set; }
}

原因很简单:Liskov substitution principle表示当IWorkerExtendedIWorkerBase的子类型时,IWorkerBase类型的任何对象都可以替换为IWorkerExtended类型的对象。现在考虑一下:

IWorkerBase baseWorker = GetExtendedWorker();
baseWorker.Settings = new SettingsBase(); // not extended

这仅适用于SettingsBase Settings IWorkerBase SettingsBase类型ISettingsBase保证的类型ISettingsBase IWorkerExtended的子类型Settings )。因此,为了将扩展工作者分配给基础工作者类型,需要保证可以为其设置// server should keep session data for AT LEAST 1 hour ini_set('session.gc_maxlifetime', 3600); // each client should remember their session id for EXACTLY 1 hour session_set_cookie_params(3600); session_start(); // ready to go! 。但如果它只是实现session_start(); $timeout = 7200; // Number of seconds until it times out. // Check if the timeout field exists. if(isset($_SESSION['timeout'])) { // See if the number of seconds since the last // visit is larger than the timeout period. $duration = time() - (int)$_SESSION['timeout']; if($duration > $timeout) { // Destroy the session and restart it. session_destroy(); session_start(); } } // Update the timout field with the current time. $_SESSION['timeout'] = time(); ,您可以将更具体的扩展设置分配给@echo off & Setlocal EnableDelayedExpansion Set /A TotalNumLines=0 ( For /R %1 %%F In (*.Txt) Do ( For /F %%N In ('Find /V /C "" ^<"%%F"') Do ( Echo %%F %%N Set /A TotalNumLines+=%%N ) ) Echo Total number of code lines for all files = !TotalNumLines! ) > log.txt EndLocal

答案 1 :(得分:0)

接口意味着隐藏实现细节,而不是告诉消费者&#34;哦 - 是 - 这个家伙有一些额外的字段&#34;。

通常,像if (worker is IWorkerExtended)这样的投射或类型检查表明您根本不需要接口。

我想说在这种情况下,你最好只有一堆worker类实现一个空的IWorker接口。

类似的东西:

public interface IWorker
{
}

public class SettingsBase
{
    public string Name { get; set; }
    public DateTime TimeStamp { get; set; }
}

public class ExtendedSettingsA : SettingsBase
{
    public string FilePath { get; set; }
}

public class ExtendedSettingsB : SettingsBase
{
    public string SomeOtherProp { get; set; }
}

public class WorkerBase : IWorker
{
    public SettingsBase Settings { get; set; }
}

public class WorkerExtendedA : IWorker
{
    public ExtendedSettingsA Settings { get; set; }
}

public class WorkerExtendedB : IWorker
{
    public ExtendedSettingsB Settings { get; set; }
}


public class XmlLoader
{
    public IWorker Load(string xml)
    {
        return null; // instance of WorkerBase or WorkerExtendedA or WorkerExtendedB
    }
}

public class Consumer
{
    public void Process(IWorker w)
    {
        if (w is WorkerBase)
        {
            WorkerBase wbase = w as WorkerBase;
            string name = wbase.Settings.Name;
            DateTime t = wbase.Settings.TimeStamp;
        }
        if (w is WorkerExtendedA)
        {
            WorkerExtendedA wa = w as WorkerExtendedA;
            string name = wa.Settings.Name;
            DateTime t = wa.Settings.TimeStamp;
            string f = wa.Settings.FilePath;
        }
        if (w is WorkerExtendedB)
        {
            WorkerExtendedB wb = w as WorkerExtendedB;
            string name = wb.Settings.Name;
            DateTime t = wb.Settings.TimeStamp;
            string other = wb.Settings.SomeOtherProp;
        }
    }
}