13

我正在尝试通过创建 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从约束中删除会导致另一个构建错误(我也不明白:()。

方法“Tests.DAL.Tenants.DbSetMock.Create<TDerivedEntity>()”的类型参数“TDerivedEntity”的约束必须匹配接口方法“System.Data.Entity.IDbSet<BusinessLayer”的类型参数“TDerivedEntity”的约束。 DAL.Tenants.Tenant>.Create<TDerivedEntity>()'。考虑改用显式接口实现。

任何人都可以帮助我成功地建立这门课吗?

4

4 回答 4

17

由于TDerived类型参数被约束为 a Tenant,因此添加约束classorstruct是多余的。只需删除class约束。

更新:奇怪的是这里的编译器错误之间似乎存在冲突。如果你“修复”一个,你就会得到另一个,陷入绝望的无限循环。幸运的是,第二个错误也为我们提供了一条出路:您可以使用显式接口实现:

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();
    }

}
于 2013-01-19T19:36:41.243 回答
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是冗余代码。

于 2013-01-19T19:36:51.823 回答
2

目前框架中只有三个可继承类,其后代可能是值类型:ObjectValueTypeEnum。所有这三种类型都是类类型,但任何派生自ValueTypeEnum将是值类型的类型,以及任何Object非派生自ValueType的类型都将是类类型。对于上述以外的任何类型,a classorstruct约束要么是多余的,要么是矛盾的;并非巧合的是,C# 不允许直接为上述类型指定约束。

在某些语言和框架中,流行的设计理念是,如果存在一种特定的表达形式,而适用于该通用形式的行为将毫无用处,则语言/框架设计者没有理由不遗余力地禁止这样的形式。在这样Fnord)的哲学下,将泛型类型限制为密封类型是完全合法的(例如,但是由于将通用约束的正常解释应用于这种情况会产生合理的行为,并且由于可以想象在某些情况下这种约束可能有用(例如,编写代码以使用正在开发且目前已密封的类,但是可能会或可能不会在其最终版本中密封,或者编写代码以与期望特定泛型形式的基于反射的代码接口),哲学将表明将泛型类型约束到密封类应该是合法的。

在其他一些语言和框架中,有一种不同的理念:如果程序员可能期望某种特定形式的构造能够提供超出一般形式的特性,但事实并非如此,并且如果没有这些特性,这种特定形式似乎不会很有用,该语言应该禁止它,即使该结构具有明确定义的精确含义,并且如果该语言的实现者没有看到程序员想要表达该实际含义的理由,则无法通过其他方式表达。

C# 和 .net 在将一个类型参数约束到另一个类型参数方面都没有任何问题,即使该其他参数的类型不会被接受为约束,这表明该限制是由语言人为施加的,原因是上述哲学。不幸的是,恕我直言,因为在很多情况下能够说出来会有所帮助,例如

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

即使 .net 可以有效地允许这样的构造,并且即使阻止 C# 排除它的唯一障碍代码显式查找此类约束以禁止它们,C# 设计者还是决定禁止此类构造而不是允许它们表现得像 .net 会解释它们(这意味着它HasAnyFlags不能直接用 a 做任何T它不能用 做的事情System.Enum,并且使用 aT作为 aSystem.Enum通常不会比使用 a 快System.Enum(有时更慢),但T仍然可以用于几个原因:

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

尽管如此,指定此类约束可能很有用,但在 C# 中指定它们的唯一方法是让 C# 源代码指定一些可用作约束的虚拟类型,然后通过实用程序运行编译后的代码,该实用程序将将所有对虚拟类型的引用替换为对首先要使用的类型的引用。

于 2013-01-20T19:03:26.360 回答
1

它告诉你的是约束:

class, Tenant

是多余的。您可以只删除class,因为Tenant它比class和包含更多约束class

于 2013-01-19T19:37:31.740 回答