【问题标题】:Inference from Generic Type Question从泛型问题推断
【发布时间】:2010-11-18 17:04:13
【问题描述】:

我想这更像是一个公开的咆哮,但为什么我不能让 c# 推断我的 Id 的类型?

public EntityT Get<EntityT>(IdT id) where EntityT : EntityObject<IdT>

和一个定义的 EntityObject 与一个 Guid 作为一个 Id 如下:

public Foo : EntityObject<Guid>

继承自抽象EntityObject类定义如下:

public abstract class EntityObject<IdT>
{
    public IdT id { get; set; }
}

get 方法的用法如下:

IRepository repository = new Repository();
var hydratedFoo = repository.Get<Foo>(someGuidId);

已编辑以提供进一步的说明。

【问题讨论】:

    标签: c# generics types inference


    【解决方案1】:

    很难说你只给出了两个声明,而不是你如何使用它们。 IdT 是另一个类型参数吗? (如果它是 TId,则表明它是 - 但事实上您使用 EntityT 作为另一个类型参数,这与惯例相反,这表明 IdT 可能也是如此......)

    现在,假设在您的情况下IdT 实际上是Guid,编译器应该如何计算出您的意思是Foo?可能还有其他类型源自EntityObject&lt;Guid&gt;

    简而言之,你没有给我们足够的信息来确定任何事情,但听起来你基本上对编译器提出了不合理的要求。

    编辑:好的,这是我对你的猜测,使用正常的命名约定:

    public interface IRepository
    {
        TEntity Get<TEntity, TId>(TId id) where TEntity : EntityObject<TId>
    }
    
    public abstract class EntityObject<TId>
    {
        public IdT id { get; set; }
    }
    
    public class Foo : EntityObject<Guid> {} 
    

    你想做的:

    IRepository repository = GetRepositoryFromSomewhere();
    Foo foo = repository.Get<Foo>(someGuid);
    

    而目前你必须这样做:

    Foo foo = repository.Get<Foo, Guid>(someGuid);
    

    是的,编译器让你稍微变得比必要的更难。整整 6 个额外字符,以使语言更简单,类型推断规则更易于理解。

    基本上,类型推断是全有或全无的事情——所有类型参数都被推断出来,或者它们都不被推断出来。这使它变得简单,因为您不需要确定哪些是指定的,哪些不是。这是问题的一部分,另一部分是你只能表达对方法的类型参数的约束——你不能:

    class Repository<TEntity>
    {
        TEntity Get<TId>(TId id) where TEntity : EntityObject<TId>
    }
    

    因为这是限制TEntity,而不是TId。同样,这类事情使类型推断更简单。

    现在你可以写:

    Foo foo = repository.Get(someGuid).For<Foo>();
    

    使用适当的Get 方法和额外的接口。我想我个人更喜欢使用Get&lt;Foo, Guid&gt;

    【讨论】:

    • 乔恩,我很抱歉没有添加更多细节。同样,这是一个更即兴的咆哮,而不是一个真正合理的问题。但是恕我直言,编译器应该能够在编译时从 foo 对象中确定 IdT。很可能是我对泛型的假设导致我对编译器可以/应该如何读取它的解释有缺陷,但我认为泛型类型直到编译时才确定,此时编译器将链接模板化对象.假设,确定被引用对象的类型不是更进一步吗?
    • 泛型 != 模板。您可能可以让 C++ 编译器“推断”这类事情,但只要泛型是运行时的,如果没有更明确的泛型定义,我就不会看到它发生。
    • IdT 不是Get 的类型参数——它只有一个类型参数EntityT。你还没有给出 IRepository 的声明,或者什么对你不起作用。请给出一个完整的示例,展示您尝试做什么,并告诉我们会发生什么而不是您想要什么。
    • 我更喜欢使用 public class RepositoryBase { } public class FooRepository : RepositoryBase { }
    • @KeeperOfTheSoul:同意——但是你会如何使用类型推断?我试图尽可能少地干扰原始设计。
    【解决方案2】:

    类似的声明

    public EntityT Get<EntityT>(IdT id) where EntityT : EntityObject<IdT>
    

    要求 IdT 是一种具体类型。如果您还想参数化 IdT,则需要使用

    public EntityT Get<EntityT, IdT>(IdT id) where EntityT : EntityObject<IdT>
    

    但这可能不是你想要的。

    【讨论】:

      【解决方案3】:

      这就是为什么我几乎放弃了具有通用实体的通用键类型。我无法弄清楚如何让我的实体拥有通用键类型,而不会将这两种类型洒在整个地方。现在我已经确定了整数键(反正我到处都有),但感觉不对。

      【讨论】:

      • 没错!目前我们一直在使用 Guid,但现在我们需要合并一个遗留数据库,我们现在正在处理复合 ID 的想法。糟糕。
      • 你想发现类型推断。调用泛型方法时不需要显式。
      • @Rob:嗯,有时你不会,有时你会。这取决于具体情况。
      【解决方案4】:

      如果您的方法签名如下所示:

      public TEntity Get<TEntity, TId>(TId id) where TEntity : EntityObject<TId>
      

      编译器可以使用一些东西...

      然后你调用 get 类似的东西:

      编辑(我错了):Product p = Get(id);

      Product p = Get<Product, Guid>(id);
      

      Jon 用他的帖子顶了这个答案,所以我会闭嘴并爬回我的洞里。

      【讨论】:

      • 这确实有效,但让每个人都痛苦地意识到 Foo 的密钥是一个 Guid。
      • @n8wrl 我不明白你的评论。
      • True Rob,感谢您的回答,但是 Repository.Get(someGuid) 将要求开发人员知道每个实体的 Id 类型。它也缺乏 Repository.Get(someGuid) 所具有的语法甜味。
      • 我不明白这是怎么回事……即使在 C++ 中你也需要它。
      • 不,id(&实体)类型将由编译器推断。
      猜你喜欢
      • 2021-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多