Convert an object that implements the interface, in its own interface

2

I made the following code:

using System;

public class Program
{
    public static void Main()
    {
        //Este método funciona
        Metodo(new ClasseTeste(){ Obj = new Registro(){ Nome = "Nome Teste"}});

        //Este não
        Metodo2(new ClasseTeste(){ Obj = new Registro(){ Nome = "Nome Teste2"}});

        //Este não          
        Metodo3(new ClasseTeste(){ Obj = new Registro(){ Nome = "Nome Teste3"}});

    }


    public static void Metodo<T>(IBase<T> parametro) where T: class,IRegistro
    {
        string nome = parametro.Obj.Nome;
        Console.WriteLine(nome);
    }

    public static void Metodo2(IBase<IRegistro> parametro)
    {
        string nome = parametro.Obj.Nome;
        Console.WriteLine(nome);
    }

    public static void Metodo3(Teste<IRegistro> parametro)
    {
        string nome = parametro.Obj.Nome;
        Console.WriteLine(nome);
    }
}

Registration:

public interface IRegistro
{
    string Nome {get;set;}
}

public class Registro : IRegistro
{
    public string Nome {get;set;}
}

Generic interface and implementation:

public interface IBase<T> where T : class
{
    T Obj {get;set;}
}

public abstract class Teste<T> : IBase<T> where T : class, IRegistro
{
    public T Obj {get;set;}
}

public class ClasseTeste : Teste<Registro>
{

}

The first method Metodo works, the other two do not.

The following error is returned:

  

Cannot convert ClasseTeste to IBase<IRegistro>

Question:

Why can not I convert an object that implements the interface, in its own interface?

I put it in DotNetFiddle

Edit:

After reading about Variance in generic interfaces (C #)

I changed the code to a covariant interface:

using System;

public class Program
{
    public static void Main()
    {

        Metodo(new ClasseTeste(){ Obj = new Registro(){ Nome = "Teste 1"} });

        Metodo2(new ClasseTeste(){ Obj = new Registro(){ Nome = "Teste 2"} });

        //Metodo3(new ClasseTeste());

    }


    public static void Metodo<T>(IBase<T> parametro) where T: class,IRegistro
    {
        string nome = parametro.GetObj().Nome;
        Console.WriteLine(nome);
    }

    public static void Metodo2(IBase<IRegistro> parametro)
    {
        string nome = parametro.GetObj().Nome;
        Console.WriteLine(nome);
    }

    public static void Metodo3(Teste<IRegistro> parametro)
    {
        string nome = parametro.GetObj().Nome;
        Console.WriteLine(nome);
    }
}

public interface IRegistro
{
    string Nome {get;set;}
}

public class Registro : IRegistro
{
    public string Nome {get;set;}
}


public interface IBase<out T> where T : class, IRegistro
{
    //T Obj{get;set;} //Erro (O Parametro T precisa ser invariante. T é Covariante
    T GetObj ();

    //void MetodoX(T obj); //Erro (O Parametro T precisa ser invariante. T é Covariante
}

public abstract class Teste<T> : IBase<T> where T : class, IRegistro
{
    public T Obj {get;set;}

    public T GetObj ()
    {
        return this.Obj;
    }
}

public class ClasseTeste : Teste<Registro>
{

}

DotNetFiddle

  

The method that expects IBase<IRegistro> will accept ClasseTeste , but in the interface I can not declare properties or methods with generic type parameters.

    
asked by anonymous 11.03.2018 / 18:57

1 answer

3

Changing this line works:

public class ClasseTeste : Teste<IRegistro>

Another possibility is to make the parameters of Metodo2() and Metodo3() :

IBase<Registro>

There is no variance in generic types. Then you can not accept IRegistro and send Registro as it does with "simple" types, that is, Teste<Registro> is not derived from Teste<IRegistro> , only Registro is derived from IRegistro .

When you use T we're not talking about inheritance, just parametric polymorphism, the compiler creates an indirection to access the actual instance. When using IBase<IRegistro> it will attempt to access a IRegistro object that does not exist, so it can only be IBase<Registro> . Unless you say you want the interface in the declaration of the concrete type, then the compiler knows that there might be an inheritance in the generic type.

One thing that many do not understand is that using a type means that any access to the object can only be done on members of that type. Try to receive any object like object and try to access a member of this object other than public members of type Object . It does not. Everything is there, you know it, but the compiler does not. It can only guarantee type security if you programmer ensures that it will only access members of type Object . And the way to ensure this for the compiler is not using (it does not let you use).

So when you say you will get Registro but pass IRegistro nothing guarantees that the object that is certainly a IRegistro is a Registro , it can be a Registro2 that conforms to IRegistro , but is not equal to Registro . Teste<> can access any members of Registro , but if it receives a Registro2 that does not have any member that Registro it will give error and break type security.

I know, you know that everything will be ok, we are seeing this in code, a compiler that only generates monolithic executable analyzing every source that generates it can do this, although it is a complex and expensive operation, but a normal compiler that does not have this guarantee can not know if it is safe.

  

When I have time I can put some example, although it will be more or less that of the question, it would be good to have some feedback of what was not clear, the AP has experience and probably will understand, but not who is less experienced

    
11.03.2018 / 19:49