【问题标题】:c# Implicit Generic Types with 2 Keys [duplicate]c#带有2个键的隐式泛型类型[重复]
【发布时间】:2020-08-04 22:50:18
【问题描述】:

我正在为 C# 和 Entity Framework 编写一个通用存储库,并且我试图在不添加一大堆额外接口的情况下让该代码正常工作。

基本上,我有一个名为 HavePrimaryKey<TKey> 的类,所有数据库实体都继承自该类:

public class MemberInfo : HavePrimaryKey<int>

如您所见,它们可以定义类使用的主键类型。

这让我可以这样做:

public interface IDbGenericIdRepository<T, TKey> : IDbGenericRepository<T>
    where T : HavePrimaryKey<TKey>

但是,这意味着我需要在创建存储库时定义 EACH 和 EVERY 键:

IDbGenericIdRepository<MemberInfo, int>

实际上,如果能够让编译器根据第一种类型必须从第二种类型继承的事实来暗示第二种类型会更好:

IDbGenericIdRepository<MemberInfo>

例如在上述情况下,由于 MemberInfo 继承自 HavePrimaryKey&lt;int&gt;,那么它希望会隐含地发现它需要一个 int ......但它只是抱怨说“需要 2 个类型的参数”

解决此问题的一种方法是为每种类型使用不同的存储库:

public interface IDbGenericIntIdRepository<T> : IDbGenericRepository<T>
    where T : HavePrimaryKey<int>

public interface IDbGenericStrIdRepository<T> : IDbGenericRepository<T>
    where T : HavePrimaryKey<string>

等等...但我希望可能有一种不那么蛮力的方法来管理这个?

有什么想法吗?

非常感谢,

菲尔。

【问题讨论】:

  • 不幸的是,编译器无法读懂你的想法。但是,这里的主要问题是您在 EF 上使用通用存储库。有点像逃离马戏团加入孤儿院
  • 在你的HavePrimaryKey 类中,你需要它是T 还是你可以使用Type?即在你的代码中你没有做typeof(T)?
  • 你打算如何使用 HavePrimaryKey 接口?
  • 你能分享你的HavePrimaryKey课程吗?另外,只是为了确认您使用的是EntityFramework 还是Entity Framework Core
  • 是的,我只会做一个接口,而不是像这样的基类。尽管正如上面的人指出的那样,基于 EF 的通用 repo 有点反模式

标签: c# .net entity-framework .net-core repository-pattern


【解决方案1】:

我了解您的思维过程,您的界面与我过去尝试过的非常相似。不过,本指南对此进行了总结:

Generic Interfaces - 只要类参数列表提供接口所需的所有参数,泛型类就可以实现泛型接口或封闭构造的接口

他们这么说很好,但在实践中这是有道理的......使用你的定义:

public interface IDbGenericIdRepository<T, TKey> : IDbGenericRepository<T>
    where T : HavePrimaryKey<TKey>

然后我们明确定义T的通用条件on另一个通用条件TKey。这意味着我们需要显式定义类型参数TKey,以便编译器能够检查 T 的类型是否有效。

我们在这里创建了一个依赖,TKeyT不是同一个类型,T但是必须实现HavePrimaryKey的一个版本,这个接口并不关心@987654330的实际类型具体是什么@ 是,只有实现接口的类必须定义它。

你仍然想知道为什么虽然你不...

就接口继承而言,我们需要注意的是,实现一个(或多个)接口的类实际上可以为每个接口的每个成员呈现不同的实现,并为类本身提供不同的实现!

所以现在我们将介绍HavePrimaryKey&lt;TKey&gt; 的简单实现以及一些以不同方式实现此接口的示例类,它们在编译器的眼中都是有效的。

public interface HavePrimaryKey<TKey>
{
    T PrimaryKey { get; }
}

public class MemberInfoInt : HavePrimaryKey<int>
{
    public int PrimaryKey { get; }
}

public class MemberInfoString : HavePrimaryKey<string>
{
    public int PrimaryKey { get; }
    string HavePrimaryKey<string> PrimaryKey { get; }
}

另一个有效的实现,但荒谬的是:

public class MemberInfo3 : HavePrimaryKey<int>, HavePrimaryKey<Guid>
{
    public string PrimaryKey { get; }
    int HavePrimaryKey<int>.PrimaryKey { get; }
    Guid HavePrimaryKey<Guid>.PrimaryKey { get; }
}

所以现在如果我们尝试使用您喜欢的表示法来实现它,我们可以开始看到两难境地:注意,这是无效的

public class Repo : IDbGenericIdRepository<MemberInfo3> ...

在这种情况下,编译器尝试评估 typeof(MemberInfo3) == HavePrimaryKey&lt;?&gt; 并在那里失败,它甚至无法编译应该用于验证第一个表达式的表达式。

没有办法 (除了我们已经知道的一种合法方式) 告诉编译器我们打算使用哪种特定类型实现来验证 MemberInfo3 是否适用于某个条件为T


C# 是强类型的,这就是我们都喜欢(或讨厌)它的地方,所以在可能存在歧义的地方,我们的天性就是强制并期望我们的类型和类有一个强类型的定义。

这就是为什么您必须提供来自继承接口的所有泛型类型参数。

【讨论】:

    猜你喜欢
    • 2020-02-11
    • 2019-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-07
    • 1970-01-01
    相关资源
    最近更新 更多