Невозможно указать как класс ограничения, так и ограничение класса или структуры

Я пытаюсь обойти насмешливую проблему, создав собственный макет IDbSet.

Пользовательский макет:

public class DbSetMock : IDbSet<Tenant>
{
    /* hidden all other implemented methods/properties */

    public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, Tenant
    {
        throw new NotImplementedException();
    }
}

Метод create дает ошибку сборки, о которой я не знаю, как решить:

не может указывать как класс ограничений, так и ограничение 'class' или 'struct'

Просто удаление class из результатов ограничений приводит к другой ошибке сборки (что я также не понимаю:().

Ограничения для параметра типа "TDerivedEntity" метода "Tests.DAL.Tenants.DbSetMock.Create <TDerivedEntity> ()" должны соответствовать ограничениям для параметра типа "TDerivedEntity" метода интерфейса "System.Data.Entity.IDbSet < BusinessLayer.DAL.Tenants.Tenant > .Create <TDerivedEntity> ()". Вместо этого рассмотрите использование явной реализации интерфейса.

Может кто-нибудь помочь мне успешно построить этот класс?

Ответ 1

Поскольку параметр типа TDerived ограничен Tenant, добавление ограничений class или struct является избыточным. Просто удалите ограничение class.

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

public class DbSetMock : IDbSet<Tenant>
{

    TDerivedEntity IDbSet<Tenant>.Create<TDerivedEntity>()
    {
        throw new NotImplementedException();
    }

}

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

public class DbSetMock : IDbSet<Tenant>
{

    TDerivedEntity IDbSet<Tenant>.Create<TDerivedEntity>()
    {
        return Create<TDerivedEntity>();
    }

    public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : Tenant
    {
        throw new NotImplementedException();
    }

}

Ответ 2

Попробуйте удалить class из части метода, например:

public class DbSetMock : IDbSet<Tenant>
    {
        /* hidden all other implemented methods/properties */

        public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : Tenant
        {
            throw new NotImplementedException();
        }
    }

class, Tenant является избыточным кодом.

Ответ 3

В структуре в настоящее время есть только три наследуемых класса, потомками которых могут быть типы значений: Object, ValueType и Enum. Все три типа являются типами классов, но любой тип, полученный из ValueType или Enum, будет типом значения, и любой тип, полученный из Object, который не является производным от ValueType, будет типом класса. При любом типе, отличном от указанного выше, ограничение class или struct будет либо избыточным, либо противоречивым; не случайно, С# запрещает прямые ограничения для указанных типов.

В некоторых языках и структурах преобладающая философия дизайна заключается в том, что если существует определенная форма выражения, в которой поведение, применимое к этой общей форме, было бы бесполезным, нет никаких оснований для того, чтобы разработчик языка/структуры не мог выйти из способ запретить такую ​​форму. В соответствии с такой философией было бы совершенно законно иметь общий тип, ограниченный закрытым типом (например, Fnord). Такая вещь была бы бессмысленной, если бы рассматриваемый тип был запечатан, и никакой будущей версии не было бы иначе, но поскольку применение обычной интерпретации общих ограничений к такой ситуации привело бы к разумному поведению, и поскольку, возможно, могут быть некоторые ситуации, когда такие ограничения могут быть полезными (например, писать код для использования класса, который находится в разработке и в настоящее время запечатан, но может или может не запечатываться в его окончательном выпуске или писать код для взаимодействия с кодом на основе Reflection, который ожидает конкретные общие формы), философия предполагает, что ограничение типичного типа на закрытом классе должно быть законным.

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

Тот факт, что ни С#, ни .net не имеет проблемы с тем, что один параметр типа привязан к другому, даже если этот другой параметр относится к типу, который не принимается в качестве ограничения, предполагает, что ограничение искусственно навязывается языка из-за вышеупомянутой философии. Это печально, ИМХО, поскольку есть много ситуаций, когда было бы полезно иметь возможность сказать, например.

bool HasAnyFlags<T>(this T enum1, T enum2) where T:struct,System.Enum

и хотя .net с пользой допускает такую ​​конструкцию, и хотя единственное препятствие, которое препятствует тому, чтобы С# исключало его код, чтобы явно искать такие ограничения, чтобы запретить их, дизайнеры С# решили запретить такие конструкции, а не позволяют им вести себя так, как .net будет их интерпретировать (это означает, что HasAnyFlags не может ничего сделать непосредственно с T, который он не мог бы сделать с System.Enum, а использование T как System.Enum как правило, не быстрее, чем при использовании System.Enum (иногда медленнее), но T может, тем не менее, быть полезным по нескольким причинам:

  • В момент компиляции метод может принудительно установить, что параметры должны быть * одинаковыми * перечисляемыми типами
  • Метод может использовать статический класс `EnumEvaluator` для создания и кэширования статических делегатов типа` Func`, так что `HasAnyFlags (T enum1, T enum2)` может быть реализован как `return EnumEvaluator.HasAnyFlags(enum1, enum2 ); `. Такая функция может быть более чем в десять раз быстрее, чем `Enum.HasFlag`.

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

Ответ 4

Это говорит о том, что ограничение:

class, Tenant

является избыточным. Вы можете просто удалить class, поскольку Tenant больше ограничений, чем class и включает class.