Razbezhkin
@Razbezhkin
программист, преподаватель

Почему не работает приведение интерфейса в тип?

Здравствуйте.
В определенном случае не происходит преобразование (cast) интерфейса в класс.
Для начала типовой рабочий пример:

Есть интерфейс, есть реализующий класс, есть еще один класс со статическим методом, который на вход получает интерфейс и через рефлексию получает имя класса. Так же этот метод выполняет приведение интерфейса к типу и обращается к свойству, определенному в типе.

public interface ITest { }

public class CTest : ITest
{
    public string Message { get; set; }
}

public class Testing
{
    public static void DoIt(ITest t)
    {
        Console.WriteLine(t.GetType().Name);//->CTest
        CTest ct = (CTest)t;
        Console.WriteLine(ct.Message);//->test message
    }
}


В методе Main консольного приложения вызывается такой код:

CTest t=new CTest(){ Message = "test message"};
Testing.DoIt(t);


результат ожидаем:

CTest
test message

---------------------

Теперь странный пример, объяснить который я прошу.

Есть Nuget пакет MassTransit (7.3.0) (https://github.com/MassTransit/MassTransit)
Там есть интерфейс Fault, от него унаследован Fault<out T>, от него унаследован класс FaultEvent<T>
где-то в недрах кода инстанциируется класс (мы же не можем создать объект интерфейса) и затем он передается в качестве входного параметра в мой метод:

public Task Consume(ConsumeContext<Fault> context)
{
    Fault m = context.Message;
    string typename = m.GetType().FullName;
}


В переменной typename оказывается значение: "GreenPipes.DynamicInternal.MassTransit.Fault", т.е. значение интерфейса, а не класса. Получается что этому коду удалось таки создать объект интерфейса.

Может ли кто-то объяснить, как такое возможно?
Спасибо.
  • Вопрос задан
  • 122 просмотра
Решения вопроса 1
ayazer
@ayazer
Sr. Software Engineer
https://github.com/MassTransit/MassTransit/blob/5e...

вот это не тот Fault про который вы думаете. Где-то в недрах GreenPipes есть еще один класс Fault. Так что никакий магии не случилось -

using MassTransit;
using System;

namespace GreenPipes.DynamicInternal.MassTransit
{
    public class MyFault<T> : Fault<T>
    {
        public T Message => throw new NotImplementedException();

        public Guid FaultId => throw new NotImplementedException();

        public Guid? FaultedMessageId => throw new NotImplementedException();

        public DateTime Timestamp => throw new NotImplementedException();

        public ExceptionInfo[] Exceptions => throw new NotImplementedException();

        public HostInfo Host => throw new NotImplementedException();

        public string[] FaultMessageTypes => throw new NotImplementedException();
    }

    public class Fault: MyFault<object>
    {

    }


    public class Program
    {
        static void Main(string[] args)
        {
            var fault = new Fault();

            string typename = fault.GetType().FullName; //GreenPipes.DynamicInternal.MassTransit.Fault

        }
    }
}
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
@KislyFan
инженер, связист и просто любитель выпить
Потому что тут так заведено. Когда ты делаешь так void DoIt(ITest t) то ты не передаешь интерфейс, а реализацию. Например тот же CTest, или его потомок, который естественно может быть формализован или приведен к CTest. А так ayazer прав
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы