【问题标题】:Cannot specify both a constraint class and the 'class' or 'struct' constraint不能同时指定约束类和“类”或“结构”约束
【发布时间】:2013-01-23 01:57:33
【问题描述】:

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

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

谁能帮我成功搭建这个类?

【问题讨论】:

  • 我有兴趣了解错误消息告诉您如何解决问题的情况 - 您应该考虑使用显式接口实现,如错误所示 - 并且然而,即使信息告诉你该怎么做,你最终还是会在互联网上向人们寻求帮助。编译器团队可以做些什么来让您自己解决问题?
  • 可能没什么,我根本不够聪明,无法解决这些琐碎的问题。我真诚地向我伟大的思想领袖道歉。
  • @EricLippert 那么在这种情况下没有办法通过公共方法实现该接口方法吗?
  • @Haxx:我想帮忙。没有必要讽刺。如果错误消息不清楚或没有帮助,那么我可以提倡比您更容易地对其进行更改。
  • @svick:我不知道;我没有考虑太多。我不熟悉那个界面。

标签: c# generics


【解决方案1】:

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

}

【讨论】:

  • 请检查问题,更新了从约束列表中删除“类”的结果。谢谢
  • Jordao,我希望我能感谢您,而不仅仅是接受您的回答。 /优雅地鞠躬!非常感谢,我可以继续了!
【解决方案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 是冗余代码。

【讨论】:

  • 这会导致以下构建错误:方法'Tests.DAL.Tenants.DbSetMock.Create()'的类型参数'TDerivedEntity'的约束必须匹配类型参数'的约束接口方法 'System.Data.Entity.IDbSet.Create()' 的 TDerivedEntity'。考虑改用显式接口实现。
  • 我会用下一个构建错误来更新问题以提高可读性
【解决方案3】:

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

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

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

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

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

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

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

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

【讨论】:

    【解决方案4】:

    它告诉你的是约束:

    class, Tenant
    

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

    【讨论】:

    • 请检查问题,更新了从约束列表中删除“类”的结果。谢谢
    猜你喜欢
    • 2019-08-12
    • 2021-02-03
    • 1970-01-01
    • 1970-01-01
    • 2016-10-29
    • 1970-01-01
    • 1970-01-01
    • 2012-09-06
    • 1970-01-01
    相关资源
    最近更新 更多