Невозможно привести 2 объекта, хотя они имеют один и тот же интерфейс - PullRequest
0 голосов
/ 02 апреля 2020

Я хотел бы отобразить 2 объекта, но я не хочу полагаться на сторонние инструменты - причина в том, чтобы помочь мне лучше понять, что могут делать эти сторонние инструменты.

У меня есть интерфейс, который имеет string Foo и bool Bar

public interface ITest
{
    string Foo { get; set; }
    bool Bar { get; set; }
}

Мой первый класс под названием A реализует ITest

public class A : ITest
{
    public string Foo { get; set; }
    public bool Bar { get; set; }
}

И, наконец, класс B также реализует ITest, но имеет дополнительное свойство

public class B : ITest
{
    public string Foo { get; set; }
    public bool Bar { get; set; }
    public int Other { get; set; }
}

Поскольку оба класса имеют общий интерфейс, как я могу привести один к другому?

var a = new A();
var b = new B();
a= b as A; //does not work but this would be ideal

Я не хочу наносить на карту, явно указав имена свойств.

Таким образом, обладание (где отображает конструктор B) также не является идеальным

var a = new A();
var b = new B(a); //not desired

Возможно ли это? Это будет означать, что будут скопированы только те свойства, которые были определены в интерфейсе.

Ответы наподобие C# отображают два сложных объекта не идеальны, так как я должен явно указать свойства.

Ответы [ 6 ]

2 голосов
/ 02 апреля 2020

Вы можете использовать отражение, чтобы автоматически определять свойства и отображать их значения.

private static T GetMappedObject<T>(object source)
{
    var output = Activator.CreateInstance(typeof(T));
    var sourceType = source.GetType();

    foreach (var outputProperty in typeof(T).GetProperties())
    {
        if (sourceType.GetProperty(outputProperty.Name) is PropertyInfo sourceProperty)
        {
            var value = sourceProperty.GetValue(source);
            outputProperty.SetValue(output, value);
        }
    }

    return (T)output;
}

Затем вы можете вызывать этот метод как var b = GetMappedObject<B>(a);.

Обратите внимание, что приведенный выше фрагмент не проверяет может ли значение исходного объекта действительно отображаться на выходное свойство, и для него требуются объекты типа T для определения конструктора без параметров.

Как указывалось в комментариях, если не для В связи с образовательным характером вашего вопроса все равно рекомендуется использовать проверенный сторонний инструмент, такой как AutoMapper , где вы можете использовать его для производственного кода.

2 голосов
/ 02 апреля 2020

Не существует неявного преобразования между A и B. Вы можете поместить их обоих в переменные типа ITest, но на этом автоматика заканчивается. C # /. NET очень консервативен с неявными преобразованиями.

И если ваш пример сработает, он также должен работать между System.String и System.Windows.Forms.Form, поскольку они оба наследуются от Object. Каждый класс в. NET неявно или явно наследует от Object, так что это было бы кошмаром.

Вам нужно либо:

  • дать обоим классам правильную функцию для преобразования. Обычно конструктор принимает экземпляр другого типа. Вы можете использовать ITest в качестве типа для этих функций. Это будет держать повторение до минимума. A(ITest initializeFrom)
  • напишите явное преобразование между этими двумя типами: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/types/casting-and-type-conversions
  • Фактически решите это, чтобы у ITest было все необходимое, поэтому поместите все свои экземпляры в переменная ITest даст вам все, что вам нужно
0 голосов
/ 02 апреля 2020

Оба класса реализуют интерфейс ITest, НО класс B также имеет дополнительное свойство, которое не позволяет выполнять приведение между A и B.

Однако у вас могут быть функции, которые обрабатывают ITest, но относитесь к ним по-разному, например

public void SaveDataFunction(ITest param)
{
    if (param is A)
    {
        SaveDataTypeA((A)param);
    }
    else if (param is B)
    {
        SaveDataTypeB((B)param);
    }
}

private void SaveDataTypeA(A value)
{
}

private void SaveDataTypeB(B value)
{
}
0 голосов
/ 02 апреля 2020

Вы можете использовать json для этого. Мы делаем это все время для преобразования DataTable в модель.

Вы сериализуете первую модель и десериализуете ее в другую. Делая это таким образом, касаются только общих полей.

        var deserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };
        string serializedObject = JsonConvert.SerializeObject(A, deserializeSettings);
        B b = JsonConvert.DeserializeObject<B>(serializedObject);
0 голосов
/ 02 апреля 2020

Вы можете использовать explicit operator, так что-то вроде этого может быть то, что вы ищете:

public class A : ITest
{
    public string Foo { get; set; }
    public bool Bar { get; set; }

    public static explicit operator A(B v)
    {
        return new A { Bar = v.Bar, Foo = v.Foo };
    }
}

И затем использовать вместо этого приведение:

a = (A)b;
0 голосов
/ 02 апреля 2020

У вас есть как минимум 2 варианта.

Либо приведение к интерфейсу ITest: b as ITest

Создание B расширение A. Таким образом, приведение b к A безопасно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...