无法同时指定约束类和“类”或“结构”约束

时间:2013-01-19 19:33:43

标签: c# generics

我正在尝试通过创建IDbSet的自定义模拟来解决mocking issue

自定义模拟:

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会导致另一个构建错误(我也不明白:()。

  

方法'Tests.DAL.Tenants.DbSetMock.Create&lt; TDerivedEntity&gt;()'的类型参数'TDerivedEntity'的约束必须与接口方法'System.Data.Entity.IDbSet&lt;的类型参数'TDerivedEntity'的约束匹配; BusinessLayer.DAL.Tenants.Tenant&GT; .Create&LT; TDerivedEntity&GT;()”。请考虑使用显式接口实现。

有人能帮助我成功建立这个课程吗?

4 个答案:

答案 0 :(得分:13)

由于TDerived类型参数被限制为Tenant,因此添加约束classstruct是多余的。只需删除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();
    }

}

答案 1 :(得分:3)

尝试从方法部分删除class,就像这样;

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

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

class, Tenant是冗余代码。

答案 2 :(得分:2)

目前,Framework中只有三个可继承的类,其后代可能是值类型:ObjectValueTypeEnum。所有这三种类型都是类类型,但从ValueTypeEnum派生的任何类型都是值类型,以及从Object派生的任何类型都不是从{{1}派生的将是一个类类型。对于上述以外的任何类型,ValueTypeclass约束将是多余的或相互矛盾的;不巧的是,C#不允许直接为上述类型指定约束。

在某些语言和框架中,流行的设计哲学是,如果存在一种特定的表达形式,其中适用于该一般形式的行为将毫无用处,那么语言/框架设计师就没有理由一路走开,禁止这样的形式。在这样的哲学下,将通用类型约束为密封类型(例如struct)是完全合法的。如果所涉及的类型被密封且没有将来的版本,这样的事情将毫无意义。否则,但由于将通用约束的正常解释应用于这种情况会产生合理的行为,并且因为可能存在这样的约束可能有用的一些情况(例如编写代码以使用正在开发的类并且目前是密封的,但可能会或可能不会被封存在其最终版本中,或者编写代码以与基于反射的代码(需要特定的通用形式)接口,哲学会建议将通用类型约束到密封类应该是合法的

在其他一些语言和框架中,不同的哲学认为:如果程序员可能期望某种特定形式的构造提供超出一般形式的特征,但它不会,并且如果该特定形式看起来不是很在没有这些特征的情况下有用,语言应该禁止它,即使构造具有明确定义的精确含义,并且如果语言的实现者没有看到程序员的理由,则无法通过其他方式表达想表达那个实际意义。

C#和.net都没有任何问题,即将一个类型参数约束到另一个类型参数,即使其他参数属于不被接受为约束的类型,也表明该限制是由人为强加的。语言由于上述哲学。不幸的是,恕我直言,因为有很多情况可以说,例如。

Fnord)

尽管.net可以有用地允许这样的构造,即使阻止C#除了代码以明确地寻找这些约束以便禁止它们的唯一障碍,C#设计者决定禁止这样的构造而不是允许他们表现为.net会解释它们(意味着bool HasAnyFlags<T>(this T enum1, T enum2) where T:struct,System.Enum 无法直接使用HasAnyFlagsT,而System.Enum无法使用T作为System.Enum的{​​{1}}通常不会比使用System.Enum(有时更慢)更快,但T可能会因某些原因而有用:

  1. 该方法可以在编译时强制参数必须是*相同*枚举类型
  2. 该方法可以使用静态类`EnumEvaluator`来生成和缓存类型为`Func`的静态委托,这样`HasAnyFlags(T enum1,T enum2)`可以实现为`return EnumEvaluator.HasAnyFlags (enum1,enum2);`。这样的功能可能是“Enum.HasFlag”的十倍以上。

仍然很有用,因为它可能是指定这样的约束,在C#中指定它们的唯一方法是让C#源代码指定一些可以用作约束的虚拟类型,然后通过实用程序将替换对虚拟类型的所有引用,并引用首先要使用的类型。

答案 3 :(得分:1)

它告诉你的是约束:

class, Tenant

是多余的。您可以删除class,因为Tenantclass更受限制,并且包含class