Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot specify both a constraint class and the 'class' or 'struct' constraint

Tags:

c#

generics

I am trying to work around a mocking issue by creating a custom mock of IDbSet.

The custom mock:

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

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

The create method gives a build error that I have no clue on how to solve:

cannot specify both a constraint class and the 'class' or 'struct' constraint

Simply removing class from the constraints results in another build error (which I also don't understand :( ).

The constraints for type parameter 'TDerivedEntity' of method 'Tests.DAL.Tenants.DbSetMock.Create<TDerivedEntity>()' must match the constraints for type parameter 'TDerivedEntity' of interface method 'System.Data.Entity.IDbSet<BusinessLayer.DAL.Tenants.Tenant>.Create<TDerivedEntity>()'. Consider using an explicit interface implementation instead.

Can anybody help me successfully building this class?

like image 644
bas Avatar asked Jan 19 '13 19:01

bas


Video Answer


3 Answers

Since the TDerived type parameter is constrained to be a Tenant, adding the constraints class or struct is redundant. Just remove the class constraint.

UPDATE: Curiously there seems to be a conflict between the compiler errors here. If you "fix" one you get the other, in an infinite loop of despair. Luckily, the second error also gives us a way out: you can use an explicit interface implementation:

public class DbSetMock : IDbSet<Tenant>
{

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

}

There seems to be no way to implement that method without using explicit interface implementation. If you need it as part of the public interface of the class, I suggest creating another method that the interface implementation forwards to:

public class DbSetMock : IDbSet<Tenant>
{

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

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

}
like image 71
Jordão Avatar answered Oct 26 '22 09:10

Jordão


Try to remove class from method part, like this;

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

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

class, Tenant is redundant code.

like image 3
Soner Gönül Avatar answered Oct 26 '22 09:10

Soner Gönül


There are presently only three inheritable classes in the Framework whose descendants may be value types: Object, ValueType, and Enum. All three of those types are class types, but any type derived from ValueType or Enum will be a value type, and any type derived from Object which is not derived from ValueType will be a class type. With any type other than the above, a class or struct constraint would either be redundant or contradictory; not coincidentally, C# disallows constraints to be directly specified for the above types.

In some languages and frameworks, a prevailing design philosophy is that if there is a particular form of expression where the behavior that applies to that general form would be useless, there's no reason for the language/framework designer to go out of the way to forbid such a form. Under such a philosophy, it would be perfectly legal to have a generic type constrained to a sealed type (e.g. Fnord). Such a thing would be pointless if the type in question were sealed and no future version would ever be otherwise, but since applying the normal interpretation of generic constraints to such situation would yield reasonable behavior, and since there might conceivably be some situations where such constraints might be useful (e.g. writing code to use a class which is in development and is presently sealed, but may or may not be sealed in its final release, or writing code to interface with Reflection-based code that expects particular generic forms), the philosophy would suggest that constraining a generic type to a sealed class should be legal.

In some other languages and frameworks, a differing philosophy holds: if a programmer might expect some particular form of a construct to offer features beyond the general form but it does not, and if that particular form wouldn't seem very useful without such features, the language should forbid it, even if the construct would have a precise meaning which was well-defined and could not be expressed via other means if the implementers of the language don't see a reason for programmers to want to express that actual meaning.

The fact that neither C# nor .net has any problem with having one type parameter constrained to another, even when that other parameter is of a type that would not be accepted as a constraint, suggests that the restriction is artificially imposed by the language due to the aforementioned philosophy. It's unfortunate, IMHO, since there are many situations where it would be helpful to be able to say, e.g.

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

and even though .net would usefully allow such a construct, and even though the only obstacle which prevents C# from excepting it code to explicitly looks for such constraints so as to disallow them, the C# designers decided to forbid such constructs rather than allow them to behave as .net would interpret them (meaning that HasAnyFlags couldn't do anything directly with a T that it couldn't do with System.Enum, and using a T as a System.Enum would generally be no faster than using a System.Enum (sometimes slower), but T could nonetheless be useful for a couple reasons:

  1. The method could enforce at compile time that the parameters must be the *same* enumerated type
  2. The method could use a static class `EnumEvaluator` to generate and cache static delegates of type `Func`, such that `HasAnyFlags(T enum1, T enum2)` could be implemented as `return EnumEvaluator.HasAnyFlags(enum1,enum2);`. Such a function could be more than ten times as fast as `Enum.HasFlag`.

Still, useful as it might be to specify such constraints, the only way to specify them in C# is to have the C# source code specify some dummy type which could be used as a constraint, and then run the compiled code through a utility which will replace all references to the dummy type with references to the type one wanted to use in the first place.

like image 2
supercat Avatar answered Oct 26 '22 08:10

supercat