简体   繁体   English

使用不在接口定义中的派生类的公共方法

[英]Using a public method of derived class that is not in interface definition

New to OOP here. 这里是OOP的新手。 I have defined an interface with one method, and in my derived class I defined another public method. 我已经定义了一个方法的接口,并且在派生类中定义了另一个公共方法。 My client code is conditionally instantiating a class of the interface type, and of course the compiler doesn't know about the method in one of the derived classes as it is not part of the underlying interface definition. 我的客户端代码有条件地实例化接口类型的类,并且编译器当然不知道派生类之一中的方法,因为它不是基础接口定义的一部分。 Here is what I am talking about: 这是我在说的:

public interface IFileLoader
{
    public bool Load();
}

public class FileLoaderA : IFileLoader
{
    public bool Load();
    //implementation

    public void SetStatus(FileLoadStatus status)
    {
        //implementation
    }

}

public class FileLoaderB : IFileLoader
{
    public bool Load();
    //implementation
    //note B does not have a SetStatus method
}

public enum FileLoadStatus
{
    Started,
    Done,
    Error
}
// client code
IFileLoader loader;

if (Config.UseMethodA) 
{
    loader = new FileLoaderA();        
}
else
{
    loader = new FileLoaderB();
}

//does not know about this method
loader.SetStatus (FileStatus.Done); 

I guess I have two questions: 我想我有两个问题:

  1. What should I be doing to find out if the object created at run-time has the method I am trying to use? 我应该怎么做才能确定在运行时创建的对象是否具有我要使用的方法? Or is my approach wrong? 还是我的方法不对?

  2. I know people talk of IOC/DI all the time. 我知道人们一直在谈论IOC / DI。 Being new OOP, what is the advantage of using an IOC in order to say, "when my app asks for an IFileLoader type, use concrete class x", as opposed to simply using an App.Config file to get the setting? 作为新的OOP,使用IOC来说“当我的应用程序请求IFileLoader类型时,使用具体的类x”,而不是简单地使用App.Config文件来获取设置,有什么好处?

Referring to your two questions and your other post I'd recommend the following: 关于您的两个问题和您的其他帖子,我建议以下内容:

What should I be doing to find out if the object created at run-time has the method I am trying to use? 我应该怎么做才能确定在运行时创建的对象是否具有我要使用的方法? Or is my approach wrong? 还是我的方法不对?

You don't necessarily need to find out the concrete implementation at runtime in your client code. 您不一定需要在运行时在客户端代码中找到具体的实现。 Following this approach you kinda foil the crucial purpose of an interface. 按照这种方法,您可能会破坏界面的关键目的。 Hence it's rather useful to just naïvely use the interface and let the concrete logic behind decide what's to do. 因此,仅天真的使用该接口并让背后的具体逻辑决定要做什么是非常有用的。

So in your case, if one implementation's just able to load a file - fine. 因此,在您的情况下,如果一个实现只能够加载文件,那就好了。 If your other implementation is able to the same and a bit more, that's fine, too. 如果您的其他实现能够做到相同甚至更多,那也很好。 But the client code (in your case your console application) shouldn't care about it and just use Load() . 但是客户端代码(在您的情况下,您的控制台应用程序)不关心它,而只需使用Load()

Maybe some code says more than thousand words: 也许有些代码说了上千个单词:

public class ThirdPartyLoader : IFileLoader
{
    public bool Load(string fileName)
    {
        // simply acts as a wrapper around your 3rd party tool
    }
}

public class SmartLoader : IFileLoader
{
    private readonly ICanSetStatus _statusSetter;

    public SmartLoader(ICanSetStatus statusSetter)
    {
        _statusSetter = statusSetter;
    }

    public bool Load(string fileName)
    {
        _statusSetter.SetStatus(FileStatus.Started);
        // do whatever's necessary to load the file ;)
        _statusSetter.SetStatus(FileStatus.Done);
    }
}

Note that the SmartLoader does a bit more. 请注意, SmartLoader的功能更多。 But as a matter of separation of concerns its purpose is the loading part. 但是作为关注点分离的问题,其目的是加载部分。 The setting of a status is another class' task: 状态的设置是另一个班级的任务:

public interface ICanSetStatus
{
    void SetStatus(FileStatus fileStatus);
    // maybe add a second parameter with information about the file, so that an 
    // implementation of this interface knows everything that's needed
}

public class StatusSetter : ICanSetStatus
{
    public void SetStatus(FileStatus fileStatus)
    {
        // do whatever's necessary...
    }
}

Finally your client code could look something like the follwing: 最后,您的客户端代码可能类似于以下内容:

static void Main(string[] args)
{
    bool useThirdPartyLoader = GetInfoFromConfig();
    IFileLoader loader = FileLoaderFactory.Create(useThirdPartyLoader);

    var files = GetFilesFromSomewhere();
    ProcessFiles(loader, files);
}

public static class FileLoaderFactory
{
    public static IFileLoader Create(bool useThirdPartyLoader)
    {
        if (useThirdPartyLoader)
        {
            return new ThirdPartyLoader();
        }

        return new SmartLoader(new StatusSetter());
    }
}

Note that this is just one possible way to do what you're looking for without having the necessity to determine IFileLoader 's concrete implementation at runtime. 请注意,这只是执行所需操作的一种可能方法,而无需在运行时确定IFileLoader的具体实现。 There maybe other more elegant ways, which furthermore leads me to your next question. 也许还有其他更优雅的方式,这进一步将我引向您的下一个问题。

I know people talk of IOC/DI all the time. 我知道人们一直在谈论IOC / DI。 Being new OOP, what is the advantage of using an IOC [...], as opposed to simply using an App.Config file to get the setting? 作为新的OOP,与仅使用App.Config文件来获取设置相比,使用IOC有什么优点?

First of all separating of classes' responsibility is always a good idea especially if you want to painlessly unittest your classes. 首先,划分班级的职责始终是一个好主意,尤其是如果您希望轻松地对班级进行单元测试。 Interfaces are your friends in these moments as you can easily substitute or "mock" instances by eg utilizing NSubstitute . 在这些时刻,接口是您的朋友,因为您可以通过使用NSubstitute轻松替换或“模拟”实例。 Moreover, small classes are generally more easily maintainable. 而且,小类通常更易于维护。

The attempt above already relies on some sort of inversion of control. 上面的尝试已经依赖于某种控制反转。 The main -method knows barely anything about how to instantiate a Loader (although the factory could do the config lookup as well. Then main wouldn't know anything, it would just use the instance). main方法几乎不了解如何实例化加载程序 (尽管工厂也可以执行配置查找。然后main不知道任何内容,它只会使用实例)。

Broadly speaking: Instead of writing the boilerplate factory instantiation code, you could use a DI-Framework like Ninject or maybe Castle Windsor which enables you to put the binding logic into configuration files which might best fit your needs. 广义地说:您可以使用像NinjectCastle Windsor这样的DI框架,而不是编写样板工厂实例化代码,它使您可以将绑定逻辑放入最适合您需要的配置文件中。

To make a long story short: You could simply use a boolean appSetting in your app.config that tells your code which implementation to use. 简而言之:您可以在app.config中简单地使用布尔值appSetting ,告诉您的代码使用哪种实现。 But you could use a DI-Framework instead and make use of its features to easily instantiate other classes as well. 但是,您可以改用DI框架,并利用其功能轻松地实例化其他类。 It may be a bit oversized for this case, but it's definitely worth a look! 对于这种情况,它可能有点过大,但是绝对值得一看!

Use something like: 使用类似:

if((loader as FileLoaderA) != null)
{
    ((FileLoaderA)loader).SetStatus(FileStatus.Done);
}
else
{
    // Do something with it as FileLoaderB type
}

IoC is normally used in situations where your class depends on another class that needs to be setup first, the IoC container can instantiate/setup an instance of that class for your class to use and inject it into your class usually via the constructor. IoC通常用于您的类依赖于另一个首先需要设置的类的情况,IoC容器可以实例化/设置该类的实例以供您的类使用,并通常通过构造函数将其注入到您的类中。 It then hands you an instance of your class that is setup and ready to go. 然后,它会为您提供已设置并准备就绪的类的实例。

EDIT: 编辑:

I was just trying to keep the code concise and easy to follow. 我只是想使代码简洁明了,易于遵循。 I agree that this is not the most efficient form for this code (it actually performs the cast twice). 我同意这不是此代码最有效的形式(它实际上执行了两次转换)。

For the purpose of determining if a particular cast is valid Microsoft suggests using the following form: 为了确定特定类型的转换是否有效,Microsoft建议使用以下形式:

var loaderA = loader as FileLoaderA;
if(loaderA != null)
{
    loaderA.SetStatus(FileStatus.Done);
    // Do any remaining FileLoaderA stuff
    return; 
}

var loaderB = loader as FileLoaderB
if(loaderB != null)
{
    // Do FileLoaderB stuff
    return; 
}

I do not agree with using is in the if. 我不同意在if中使用is。 The is keyword was designed to determine if an object was instantiated from a class that implements a particular interface, rather than if a cast is viable. is关键字旨在确定对象是否从实现特定接口的类实例化,而不是强制转换是否可行。 I have found it does not always return the expected result (especially if a class implements multiple interfaces through direct implementation or inheritance of a base class). 我发现它并不总是返回预期的结果(尤其是如果一个类通过直接实现或继承基类来实现多个接口)。

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

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