Как я могу выразить, что аргумент должен реализовать несколько интерфейсов в C#

Похоже на этот вопрос: как мне потребовать аргумент метода для реализации нескольких интерфейсов? Я хочу, чтобы аргумент метода реализовал несколько интерфейсов.

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

Подумай о файле. Может быть:

  1. читабельный => IReadable
  2. доступный для записи => IWriteable
  3. архив => IArchive
  4. автоматически генерируется => IGenerated

...

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

псевдокод:

void WriteTo( IWritable + IGenerated + IArchive file)
{
   //...
}

5 ответов

Решение

Решение, которое я нашел здесь: Как мне потребовать аргумент метода для реализации нескольких интерфейсов? с учетом C#.

Кредиты идут к Michael Myers

internal interface IF1
{
    void M1();
}

internal interface IF2
{
    void M2();
}

internal class ClassImplementingIF1IF2 : IF1, IF2
{

    public void M1()
    {
        throw new NotImplementedException();
    }

    public void M2()
    {
        throw new NotImplementedException();
    }

}

internal static class Test
{

    public static void doIT<T>(T t) where T:IF1,IF2
    {
        t.M1();
        t.M2();
    }

    public static void test()
    {
        var c = new ClassImplementingIF1IF2();
        doIT(c);
    }
}

Общее ограничение + возможно?

void WriteTo<T>( T file) where T : IWritable,IGenerated,IArchive
{
   //...
}

Используйте общие ограничения:

void WriteTo<T>(T file) where T: IWritable, IGenerated, IArchive
{
   //...
}

См. Ограничения на параметры типа (Руководство по программированию в C#)

Это решение пытается объединить идею суперкатера с хранением таких "мультиинтерфейсных" объектов.

Я сделал это, сохранив их в Holder класс, который предоставляет указанные интерфейсы объекта.

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

На положительной стороне, хотя вы также можете динамически создавать Holders и, конечно, хранить "мультиинтерфейсные" объекты.

Было бы неплохо включить holder.Get<T>() методы (лучше, чем holder.t1), но он не скомпилируется.Может быть, у кого-то есть идея, как это исправить? Я думаю, что нужно добавить ограничение T1 не является T2 и наоборот. Эти швы, относящиеся к C# generic, * не * реализуют что-то, и они не нашли решения.

internal interface IF1
{
    void M1();
}

internal interface IF1_extension : IF1
{
}

internal interface IF2
{
    void M2();
}

internal class ClassImplementingIF1IF2 : IF1_extension, IF2
{

    public void M1()
    {
        throw new NotImplementedException();
    }

    public void M2()
    {
        throw new NotImplementedException();
    }

}

internal interface Getter<T> where T : class
{
    T Get();
}

internal class Holder<T1, T2> //: Getter<T1>, Getter<T2> // not possible since T1 and T2 may be the same => won't compile!
    where T1 : class
    where T2 : class
{
    private Holder(T1 t1, T2 t2)
    {
        Debug.Assert(t1 != null, "Argument is no " + typeof(T1).Name);
        Debug.Assert(t2 != null, "Argument is no " + typeof(T2).Name);
        this.t1 = t1;
        this.t2 = t2;
    }

    public static Holder<T1, T2> CreateFrom<T>(T t) where T : T1, T2
    {
        return new Holder<T1, T2>(t, t);
    }
    public static Holder<T1, T2> CreateDynamicallyFrom(object t)
    {
        return new Holder<T1, T2>(t as T1, t as T2);
    }

    public readonly T1 t1;
    public readonly T2 t2;

    //T1 Getter<T1>.Get()
    //{
    //    return t1;
    //}
    //T2 Getter<T2>.Get()
    //{
    //    return t2;
    //}
}

internal class Holder<T1, T2, T3>  // Holder<T1,T2,T3,T4> etc. are defined in a similar way
    where T1 : class
    where T2 : class
    where T3 : class
{
    private Holder(T1 t1, T2 t2, T3 t3)
    {
        Debug.Assert(t1 != null, "Argument is no " + typeof(T1).Name);
        Debug.Assert(t2 != null, "Argument is no " + typeof(T2).Name);
        Debug.Assert(t3 != null, "Argument is no " + typeof(T3).Name);
        this.t1 = t1;
        this.t2 = t2;
        this.t3 = t3;
    }

    public static Holder<T1, T2,T3> CreateFrom<T>(T t) where T : T1, T2, T3
    {
        return new Holder<T1, T2, T3>(t, t, t);
    }
    public static Holder<T1, T2, T3> CreateDynamicallyFrom(object t)
    {
        return new Holder<T1, T2, T3>(t as T1, t as T2, t as T3);
    }

    public readonly T1 t1;
    public readonly T2 t2;
    public readonly T3 t3;

}


internal static class Test
{

    public static void doIt<T>(T t) where T : IF1, IF2
    {
        t.M1();
        t.M2();
    }

    public static void doIt(Holder<IF1, IF2> t) // Interfaces should be mentioned in alpahbetical order since Holder<IF1,IF2> != Holder<IF2,IF1>
    {
        t.t1.M1();
        t.t2.M2();
    }


    public static void doIt_extended<T1, T2>(Holder<T1, T2> t) // handles conversions from Holder<T1,T2> to Holder<T1 or base of T1, T2 or base of T2>
        where T1 : class, IF1
        where T2 : class, IF2
    {
        t.t1.M1();
        t.t2.M2();
    }

    public static void test()
    {
        var c = new ClassImplementingIF1IF2();
        doIt(c);
        var c_holder = Holder<IF1, IF2>.CreateFrom(c);
        doIt(c_holder);

        var another_c_holder = Holder<IF1_extension, IF2>.CreateFrom(c);
        doIt_extended(another_c_holder);


        object diguised_c = c;
        var disguised_c_holder = Holder<IF1, IF2>.CreateDynamicallyFrom(diguised_c);
        doIt(disguised_c_holder);

    }
}

Я не хочу генерировать IWritableGeneratedArchive, так как есть слишком много комбинаций

Как много комбинаций? Каким бы ни было количество возможных комбинаций, вам нужно создать комбинированный интерфейс только для тех, которые вы фактически используете в качестве параметра метода, и в этот момент вы все равно записываете все это. Другими словами, это на самом деле не так уж много работы.

Я хочу использовать его с некоторыми существующими классами, которые я не могу изменить.

Тип IWritableGeneratedArchive может быть передан в то, что требуется только IArchive или IWritable. Это все равно будет работать с тем, что у вас уже есть.


Но так как вы уже решили отказаться от этого, и похоже, что вы уже вложили много средств в эту архитектуру, мне интересно, видели ли вы Code Contracts. Похоже, они могут делать то, что вам нужно.

Другие вопросы по тегам