How to make compatible objects coming from two different classes, derived from the same interface?

Asked

Viewed 290 times

4

I have an interface (I’ll call it ICliente) which is used as a contract between an application and a DLL.

public interface ICliente
{
    int Id {get; set;}
    string Nome {get; set;}
    ...
}

This interface is implemented both on the application side and on the DLL side in the same way, with the same name, etc. The only difference is that on the DLL side there are some Annotations.

//Lado da Aplicação
public class Cliente: ICliente
{
    public int Id {get; set;}

    public string Nome {get; set;}
    ...
}

//Lado da DLL
public class Cliente: ICliente
{
    [Key]
    public int Id {get; set;}

    public string Nome {get; set;}
    ...
}

When I try to pass one object from the application to the DLL, the following error occurs:

"Cannot convert an object of type 'Application.Model.Client' in type 'Library.Model.Client.'"

Even when my method of assignment receives a ICliente as parameter (example below), at the moment I try to cast this object ICliente to the Cliente from the DLL, it gives the same message.

//Lado da Aplicação
public bool Salvar(ICliente objClienteApp )
{
    ...
    return DLL.Salvar((Cliente) objClienteApp); //<-- Aqui ocorre o erro
}

//Lado da DLL
public bool Salvar (Cliente objClienteDLL)
{
    ...
}

For me, because they are part of the same interface they should talk, but practice showed the opposite.

I’m trying to avoid making a "de-stop" between objects, because that would be a lot of work.

Is there any way we can make these objects compatible?

  • What exactly wasn’t clear, @Maniero?

  • His answer shows that the question had nothing to do with what he wanted.

  • The answer (in my view) shows the conceptual error I was making when trying to reconcile two different classes. The solution to my problem was to use dependency injection (which before I had only heard of) to get the result I expected.

  • This has nothing to do with DI, the answer doesn’t talk about your problem, and it just shows that you don’t understand the problem yet, besides your answer doesn’t help anyone, not even yourself, even if you think so.

  • Maniero, it’s already in place. I have an application that uses a DLL as a type of "data access plugin" using dependency injection to maintain. The application calls the DLL and the DLL returns the data according to the contract of the interfaces involved. In my view, this solved (and really this, because the program is here in my notebook running). Why do you think it has nothing to do with DI? Is there another more correct way to do this? If you have, give me a hint.

  • I answer, you ignored it. You found your solution and if it is only you could say something about it, hence the question is not clear. Any posted answer may be right or wrong, no one knows what the real doubt is (maybe only you, maybe not you), I could only be sure of that after posting the answer.

Show 2 more comments

3 answers

6

There’s at least two problems there.

Double definitions of the same object

One of them is to have two Cliente being the same object. This definition should be canonical, this will cause several problems. Most maintenance difficulties in a software are due to wrong modeling (this one in particular I had never seen). If by chance they would be different things they should never be conflicting. Fix this conceptual error which is the root of the problem and will not have the build error. Don’t try to fix it otherwise because you’ll just be changing the place error (it might even work, but it’ll still be wrong).

Fiat 147 todo detonado andando pelas ruas

Abstraction of the concrete

The other mistake is to use one cast, Almost every time you do this is because you modeled it wrong (most of the exceptions are because of the legacy since .NET modeled some things wrongly in the first version). If you really need to make one cast is because you’re getting the wrong guy, so get the Cliente and not the ICliente. Is making a cast to access other members, so the interface you need to access is not the object, so the parameter should be the class and not the interface.

I won’t even tell you to use the namespace correct to catch the Cliente correct for this case because it would still keep the error of having the same object defined twice in different places.

Interface

This is wrong use of interface, because interface are capabilities of an object so something that calls ICliente is not a capacity is the definition of an object, perhaps it was the case to use an abstract class (unfortunately rarely have good answers about it here).

I strongly suggest studying further (with depth, not just the surface) what an interface is and why to use it, here are some good answers about this (others not so much, especially on the internet has a lot of bad content that desensina, I do not know why people think that everything that is on the internet is reliable and sometimes question reliable sources).

You must accept an interface when only you need to access your members when you need to access other members then: or you need another interface; or you need to access the whole object; or you need to expand what the interface has, which is even scary to say this because in this case it would probably be another error.

Note that they are compatible while you use the interface, when you decide to do the cast is that it gives problem, but the problem is lower as it is said above.

Did you notice that you only created the interface to "group" two objects that are actually one? This interface should not even exist if it had not created two classes to represent the same object.

Conceptualization

I always say that object orientation is difficult, and one of the reasons is because it’s not about mechanisms like interface use, it’s about modeling right and it’s not usually taught anywhere and without the concrete case almost always the person does wrong (you can’t follow cake recipes). That is why it is common to have "work", is fixing the modeling errors.

The model should be unique, canonical, DRY, including to facilitate maintenance. It makes no sense to have one thing in what you call "application" and another in what you call "DLL", because this DLL is part of the application. Putting penduricalho in the application will not solve the problem, just make it more complicated.

Tomada cheia de penduricalhos para ligar com o plug em vez de por direto

You can see that there are other misconceptions looking at just this stretch, imagine seeing all the application. No correct concepts or misses without realizing or hits by coincidence.

So there must be several other problems that do not cause errors in the build. Working is different than being right.

  • Thanks for the lesson! I’m still absorbing what you wrote. Just to put it in a better context, this DLL that I put as something "outside the application", will work as a kind of "data access plugin", and the application (immutable) would only consume the DLL data. The DLL could: access a REST API, a binary file with exotic format, a Sybase database, etc. . So I used the interface: I wanted the application and DLL to have a compatible format between them. I will study the subject.

  • The moment it is used by the application it is part of the application. That’s why I say it is conceptualized wrong. If the other class Cliente has something totally different should have another name indicating that it is not a client but something that takes data from a client in "exotic format". And then to pass from one to the other you should copy manually even, at least it would be the normal case (it may be that in some case you would not need, but you need to understand the question much more deeply). Note that the interface would still be wrong because it is not the same functionality.

  • Yes, I get it. Part of the problem I solved by accessing the objects "via interface", without cast (I didn’t know it worked, but it worked). This "worked" and meets in part what I need. But you showed me that it is necessary to re-examine the system. Thank you very much.

5

The reason is very simple: one cannot implicitly convert two objects because they implement the same interface, this does not guarantee that they are equal.
Otherwise we can say that, the two classes implement the same interface, and not derive of the same type, so nothing guarantees that they are "equal" to make an explicit cast like this.

An interface says "what should be implemented" to respect the contract, but not "that only this should be implemented", so you could simply in the DLL Client class add a property public int Abobrinha and in the other class Customer public string BlaBlaBla, this would not hurt the interface implementation and shows how different classes can be.

The interface guarantees the minimum, so you already know what to expect from that class, then cast is something else.

Answering your question, for them to be compatible would need to share a common class, which would make it invalid to use the interface, because then would not be sharing a contract but a type.
You can use for these cases a Mapper to do this in a simple way, such as Automapper

It easily maps similar objects (which have the same properties and same types), for example that implement the same interface, with minimal code, I think this should help in a simple way in this problem.

A very simple example for the example of your class would be:

public bool Salvar(ICliente objClienteApp)
{
    Mapper.CreateMap<ICliente,Cliente>();

    var clienteMapeado = Mapper.Map<Cliente>(objClienteApp);
    ....
}
  • Thank you. I’ll test it and come back here to tell you if it worked.

  • Okay, any questions leave a comment. There are several free mappers on the market, I cited the Automapper because it is well known and very used in these cases :)

2


From what I’ve been going through, what I missed in my code was that dependency injection.

My mistake (according to my analysis) was to want to reference an object of a class from another object of another class (as already said), but I can reference any object through the interface that the classes of these objects implemented.

The example to follow more or less shows the idea I want to put into practice:

// define an interface
public interface ILogger
{
    void LogError(Error error);
}

// implement ILogger by logging to a file
public class FileLogger : ILogger
{
    public void LogError(Error error)
    {
        File.WriteLine(someFileUri, error.ToString());
    }
}


// implement ILogger by logging to Debug log
public class DebugLogger : ILogger
{
    public void LogError(Error error)
    {
        Debug.WriteLine(error.ToString());
    }
}

// program to the ILogger interface
public class MyProgram
{
    private ILogger _log;

    // inject whichever ILogger into this class via constructor
    public MyProgram(ILogger log)
    {
        _log = log;
    }

    public void Start()
    {
        try
        {
            StartInternal();
        }
        catch(Exception ex)
        {
            Error error = Error.FromException(ex, Context);
            _logError(error);
        }
    }
}

In this code above, I can pass to the constructor "Myprogram" both an object of the "Filelogger" class and an object of the "Debuglogger" class because both implement the same Ilogger interface

The difference is that in my case, one of the classes that implement the interface in question is in a DLL.

I would like your opinion on the answer, whether it makes sense, etc.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.