简体   繁体   中英

Implementations of an Interface with Different Types?

Searched as best I could but unfortunately I've learned nothing relevant; basically I'm trying to work around the following problem in C#...

For example, I have three possible references (refA, refB, refC) and I need to load the correct one depending on a configuration option. So far however I can't see a way of doing it that doesn't require me to use the name of said referenced object all through the code (the referenced objects are provided, I can't change them). Hope the following code makes more sense:

public ??? LoadedClass;

public Init()
    {
        /* load the object, according to which version we need... */
        if (Config.Version == "refA")
        {
            Namespace.refA LoadedClass = new refA();
        }
        else if (Config.Version == "refB")
        {
            Namespace.refB LoadedClass = new refB();
        }
        else if (Config.Version == "refC")
        {
            Namespace.refC LoadedClass = new refC();
        }

        Run();
    }
private void Run(){
    {
        LoadedClass.SomeProperty...
        LoadedClass.SomeMethod(){ etc... }
    }

As you can see, I need the Loaded class to be public, so in my limited way I'm trying to change the type 'dynamically' as I load in which real class I want. Each of refA, refB and refC will implement the same properties and methods but with different names. Again, this is what I'm working with, not by my design.

All that said, I tried to get my head around Interfaces (which sound like they're what I'm after) but I'm looking at them and seeing strict types - which makes sense to me, even if it's not useful to me.

Any and all ideas and opinions are welcome and I'll clarify anything if necessary. Excuse any silly mistakes I've made in the terminology, I'm learning all this for the first time. I'm really enjoying working with an OOP language so far though - coming from PHP this stuff is blowing my mind :-)

EDIT

Sorry for not making it clear at all, but each of refA, refB, refC has unique types for their methods, even though they're the essentially the same method functionality wise (the idea being they're versioned). This means with an interface, in my mind, I'm left with:

public interface IRef
    {
        SomeType<<RefA,RefB,RefC,???>> SomeProperty {get;}
        void SomeMethod();
    }

Thanks to GenericTypeTea for the starting Interface point. I may be trying to do something that's not recommended/not possible... The underlying problem is I need to support different versions (as per the configuration option) from the same program; or I'm going to be developing 'different' programs for each version and that's just a big horrible mess :-)

EDIT 2

public interface Sage
    {
        SageDataObject???.SDOEngine sdo;
    }

class SageObj150 : Sage
    {
        SageDataObject150.SDOEngine sdo = new SageDataObject150.SDOEngine();
    }

class SageObj160 : Sage
    {
        SageDataObject160.SDOEngine sdo = new SageDataObject160.SDOEngine();
    }

class SageObj170 : Sage
    {
        SageDataObject170.SDOEngine sdo = new SageDataObject170.SDOEngine();
    }

Where SDOEngine() (I've only just seen) is an interface itself - which I think just confused things greatly. Thought I was doing pretty well to write the whole application starting with no knowledge of C# but this now seems beyond my Google skills. Thanks in advance for everyone's patience!

Unless I've misunderstood, you just need an interface. So as long as RefA-C implement the same properties and methods, you'll be fine:

public interface IRef
{
   string SomeProperty {get;}
   void SomeMethod();
}

Then implement the interface for RefA-C:

public class RefA : IRef
{
    public string SomeProperty {get;}

    public void SomeMethod()
    {
       // Do for A
    }
}

public class RefB : IRef
{
    public string SomeProperty {get;}

    public void SomeMethod()
    {
       // Do for B
    }
}

Then you can refer to the interface as the interface of the implementation:

public IRef LoadedClass;

And instantiate it as follows:

if (UseConfigA) LoadedClass = new RefA(); // etc

If the classes doesn't have a common ancestor and have different method names you might subclass each of them, implementing a common interface creating a proxy object.

interface ICommonFunctions
{
   void MethodA();

   void MethodB();
}

class ProxyRefA : ICommonFunctions
{
    refA proxyObj = new refA;

    void MethodA() { proxyObj.methodWithOtherName(); }

    void MethodB() { proxyObj.otherMethodName(); }
}

/* The same for refB and refC */

And so on.

This way on your original code you can have:

public ICommonFunctions LoadedClass;

...

EDIT : Implementing Steven Jeuris suggestion. Using composition instead of inheritance in the proxy class.

Here's what I would do:

  1. Create an interface (let's call it ICommonStuff) that has all of the properties and methods that are shared between refA, refB and refC.
  2. For each of refA, refB and refC, create a wrapper class that accepts the type as an argument and implements the interface ICommonStuff.
  3. Implement your Init method largely as you have already, except instead of setting the LoadedClass property to a refA, refB or refC directly, set it to one of the wrappers.

And this is what the resultant code would be:

public ICommonStuff LoadedClass;

public Init()
{
    /* load the object, according to which version we need... */
    if (Config.Version == "refA")
    {
        LoadedClass = new WrapperA(new refA());
    }
    else if (Config.Version == "refB")
    {
        LoadedClass = new WrapperB(new refB());
    }
    else if (Config.Version == "refC")
    {
        LoadedClass = new WrapperC(new refC());
    }
    Run();
}
private void Run(){
{
    LoadedClass.SomeProperty...
    LoadedClass.SomeMethod(){ etc... }
}

I suggest writing one common adapter for the different classes. You then have your one common interface which handles accessing the specific named underlying property/method used.

UPDATE:

"Each of refA, refB and refC will implement the same properties and methods but with different names ."

I interpreted that the names of the properties and methods are different, but now I'm guessing you probably only mean that refA refB and refC have different names?

If you are allowed to adjust the different implementations , give them a common interface, and use this throughout your code, as mentioned in the other answers. Otherwise the adapter might still be a feasible approach, or you could create a wrapper class for every one of them, and implement the required interface.

Declare Interface in shared .NET assembly.

Create A, B and C classes inherited from the Interface and place them in different assemblies.

Use Assembly.Load() in main project to load assembly you want use. Find class inhereted from the Interface in it and create instance.

I would suggest that if the 3 different types share a common interface then they should implement a common interface. For example, IExample

Then you can load the right implementation instance from your config file.

// public fields are a no-no, use properties instead
public IExample LoadedClass { get; private set; } 

. . .

LoadedClass = (IExample)Activator.CreateInstance(Config.Version);

Where Config.Version is the full name of your class eg Namespace.RefA

You indeed need an interface for this. consider for example that each class provides a method doThis(), but RefA implements it as a(), RefB implements it as b() and RefC implements it as c(). You can think of interfaces as abstract class which cannot provide any code, but of which a class can inheret multiple.

You can make an interface as follows:

interface CanDoThis{
    public void doThis();
}

You then need to modify the class files as follows:

public class RefA : CanDoThis // this means "I implement the interface CanDoThis"
{
    // Add this method, it is needed for the interface
    public void doThis(){
        a();
    }

    public void a(){
        // this has already been provided in the origional class file
    }
}

And likewise for RefB and RefC. Your code then becomes:

public CanDoThis loadedClass;

public Init()
{
    /* load the object, according to which version we need... */
    if (Config.Version == "refA")
    {
        loadedClass = new RefA();
    }
    // etc
}

be aware that you will only be able to call methods that are defined in the interface, so each method you want to call needs to be defined in the interface.

You shouldn't need it if you program well, but if you ever want to check which class the instance is of you can just use the standard "is" just as you would with subclasses: if(loadedClass is RefA){ //... }

Looks like a good opportunity for the factory pattern to me. Create your common interface, give it to all three classes, and spin up the correct class. These are bound by the interface so you don't really have to do anything too weird.

class Program
    {
        static void Main(string[] args)
        {
            RefFactory factory = new RefFactory();
            ICommonFunctionality a = factory.Create(0);
            Console.WriteLine(a.SomeMethod());

            ICommonFunctionality b = factory.Create(1);
            Console.WriteLine(b.SomeMethod());

            ICommonFunctionality c = factory.Create(2);
            Console.WriteLine(c.SomeMethod());

            //The above is to just test. Should be something like this:
            ICommonFunctionality Ref;

            if (1 == 1)
            {
                Ref = factory.Create(0);
            }
            if (1 == 2)
            {
                Ref = factory.Create(1);
            }

            //etc..

            Console.Read();
        }
    }

    public class RefFactory
    {
        public ICommonFunctionality Create(int someCondition)
        {
            if (someCondition == 0)
            {
                return new RefA();
            }
            else if (someCondition == 1)
            {
                return new RefB();
            }
            else
            {
                return new RefC();
            }
        }
    }

    public interface ICommonFunctionality
    {
        bool SomeProperty { get; set; }
        string SomeMethod();
    }

    public class RefA : ICommonFunctionality
    {
        public bool SomeProperty { get; set; }
        public string SomeMethod()
        {
            return "RefA";
        }
    }

    public class RefB : ICommonFunctionality
    {
        public bool SomeProperty { get; set; }
        public string SomeMethod()
        {
            return "RefB";
        }
    }

    public class RefC : ICommonFunctionality
    {
        public bool SomeProperty { get; set; }
        public string SomeMethod()
        {
            return "RefC";
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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