从通用类型问题推断

发布于 2024-08-01 18:03:56 字数 587 浏览 2 评论 0原文

我想这更像是公开的咆哮,但为什么我不能让 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);

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

I suppose this is more of a public rant, but why can't I get c# to infer my Id's type?

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

and a defined EntityObject with a Guid as an Id as follows:

public Foo : EntityObject<Guid>

Inheriting from the abstract EntityObject class defined as follows:

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

Usage of the get method would be as follows:

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

edited to provide further clarification.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

半岛未凉 2024-08-08 18:03:56

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

现在,假设在您的情况下 IdT 实际上是 Guid,编译器应该如何计算出您的意思是 Foo ? 可能还有从 EntityObject 派生的其他类型。

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

编辑:好的,这是我对您所拥有的内容的猜测,使用正常的命名约定:

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

It's hard to say given that you've only given two declarations, not how you're using them. Is IdT another type parameter somewhere? (If it were TId, that would suggest it is - but the fact that you're using EntityT for another type parameter, contrary to conventions, suggests that maybe IdT is as well...)

Now, assuming IdT is actually Guid in your case, how should the compiler work out that you mean Foo? There could be other types deriving from EntityObject<Guid>.

In short, you haven't given us enough information to tell anything for sure, but it sounds like you're basically making unreasonable demands on the compiler.

EDIT: Okay, here's my guess at what you have, using normal naming conventions:

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> {} 

You want to do:

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

Whereas currently you have to do:

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

Yes, the compiler is making it very slightly harder for you than necessary. A whole 6 extra characters, for the sake of keeping the language simpler and the rules of type inference easier to understand.

Basically type inference is an all or nothing affair - either all type parameters are inferred or none of them is. That keeps it simple as you don't need to work out which ones are being specified and which aren't. That's part of the problem, and the other part is that you can only express constraints on the type parameters of the method - you can't have:

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

because that's constraining TEntity, not TId. Again, this sort of thing makes type inference simpler.

Now you could potentially write:

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

with an appropriate Get method and an extra interface. I think I'd personally prefer to just use Get<Foo, Guid> though.

沉睡月亮 2024-08-08 18:03:56

像这样的声明

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

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

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

但这可能不是您想要的。

A declaration like

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

demands that IdT is an concrete type. If you want to parameterize IdT as well, you'd need to use

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

But that's probably not what you'd want.

请叫√我孤独 2024-08-08 18:03:56

这就是为什么我几乎放弃了具有通用实体的通用键类型。 我无法弄清楚如何让我的实体拥有通用键类型而不将这两种类型散布到各处。 现在我已经选择了整数键(无论如何我到处都有它),但感觉不对。

This is why I've all but given up on generic key types with generic entities. I could not figure out how to get my entities to have generic key types without sprinkling the two all over the place. Now I've settled on integer keys (which is what I have everywhere anyway) but it feels wrong.

_蜘蛛 2024-08-08 18:03:56

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

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

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

然后您可以使用以下内容调用 get :

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

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

Jon's他的帖子将这个答案钉在了顶部,所以我会闭嘴并爬回我的洞里。

If your method signature looked like this:

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

The compiler would have something to work with...

You then call get with something like:

EDIT (I was wrong): Product p = Get(id);

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

Jon's nailed this answer with his post up top so I'll shut up and crawl back in my hole.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文