2009-08-11 43 views
6

Supongo que esto es más una diatriba pública, pero ¿por qué no puedo obtener C# para inferir el tipo de mi Id?Inferencia del tipo genérico Pregunta

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

y una EntityObject definido con un GUID como un Id como sigue:

public Foo : EntityObject<Guid> 

hereda de la clase EntityObject abstracto definen como sigue:

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

Uso del método get haría ser de la siguiente manera:

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

editado para proporcionar más aclaraciones.

Respuesta

3

Es difícil de decir dado que solo ha dado dos declaraciones, no cómo las está usando. ¿Es IdT otro parámetro de tipo en alguna parte? (Si fuera TId, eso sugeriría que es - pero el hecho de que está utilizando EntityT para otro tipo de parámetro, en contra de las convenciones, sugiere que tal vez IdT es así ...)

Ahora, suponiendo que es IdT De hecho, Guid en su caso, ¿cómo debería funcionar el compilador que quiere decir Foo? Puede haber otros tipos derivados de EntityObject<Guid>.

En resumen, no nos ha dado suficiente información para contar algo con certeza, pero parece que básicamente está haciendo demandas irracionales al compilador.

EDIT: Bueno, aquí está mi suposición de lo que tienes, usando convenciones de nombres normales:

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

Se desea realizar:

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

Mientras que actualmente hay que hacer:

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

Sí, el compilador lo está haciendo muy ligeramente más difícil para ti de lo necesario. Un total de 6 caracteres adicionales, con el objetivo de mantener el lenguaje más simple y las reglas de tipo de inferencia más fáciles de entender.

Básicamente, la inferencia de tipo es un asunto de todo o nada: todos los parámetros de tipo se deducen o ninguno de ellos. Eso lo mantiene simple ya que no necesita determinar cuáles se especifican y cuáles no. Eso es parte del problema, y ​​la otra parte es que sólo se puede expresar restricciones sobre los parámetros de tipo del método - no se puede tener:

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

porque eso limitando TEntity, no TId. De nuevo, este tipo de cosas hace que la inferencia de tipos sea más simple.

Ahora podría potencialmente escribir:

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

con un método apropiado Get y una interfaz adicional. Creo que personalmente preferiría simplemente usar Get<Foo, Guid>.

+1

Jon, me disculpo por no agregar más detalles. De nuevo, esto fue más una farsa que una verdadera pregunta legítima. Pero en el caso de que el compilador sea capaz de determinar IdT desde el objeto foo en tiempo de compilación. Lo más probable es que mis suposiciones genéricas me lleven a una interpretación errónea de cómo el compilador podría/debería leer esto, pero asumí que el tipo genérico no se determinó hasta el momento de la compilación, momento en el que el compilador vincularía el objeto con plantilla Suponiendo que, ¿no sería un paso más para determinar el tipo de objeto al que se hace referencia? – Raspar

+0

Generics! = Plantillas. Probablemente puedas obtener un compilador de C++ para "inferir" ese tipo de cosas, pero mientras los genéricos sean tiempo de ejecución, no veo que suceda sin una definición genérica más explícita. – user7116

+0

IdT no es un parámetro de tipo de 'Get', que solo tiene un parámetro de tipo,' EntityT'. No ha proporcionado la declaración de IRepository, o lo que no funciona para usted. Proporcione un ejemplo * completo *, que muestre lo que * intenta * hacer y díganos qué sucede en lugar de lo que desea. –

0

Si su firma del método era la siguiente:

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

El compilador tendría algo con qué trabajar ...

A continuación, llama a conseguir con algo como:

EDIT (que estaba incorrecto): Producto p = Get (id);

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

de Jon clavado esta respuesta con su puesto encima de la tapa por lo que callo y rastrear de nuevo en mi agujero.

+0

Esto ciertamente funciona, pero resulta dolorosamente obvio para todos que la clave de Foo es un Guid. – n8wrl

+0

@ n8wrl No entiendo tu comentario. – grenade

+0

True Rob, agradezco su respuesta, sin embargo Repository.Get (someGuid) requeriría que el desarrollador conozca el tipo de Id. De cada entidad.También le faltaría la dulzura sintáctica que tendría el Repository.Get (someGuid). – Raspar

0

Una declaración como

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

exigencias que IdT es un tipo de hormigón. Si también desea parametrizar IdT, necesitará usar

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

Pero eso probablemente no sea lo que usted desearía.

0

Es por eso que he renunciado a los tipos de claves genéricas con entidades genéricas. No pude encontrar la forma de que mis entidades tuvieran tipos de claves genéricas sin rociarlas por todos lados. Ahora me he decidido por las claves enteras (que es lo que tengo en todas partes) pero se siente mal.

+0

¡Exactamente! Por el momento hemos estado usando Guids, pero ahora que necesitamos incorporar una base de datos heredada, ahora estamos lidiando con la idea de una identificación compuesta. puaj. – Raspar

+0

Desea descubrir la inferencia tipo. No necesita ser explícito cuando llama a un método genérico. – grenade

+0

@Rob: Bueno, a veces no, y a veces lo haces. Depende de la situación exacta. –

Cuestiones relacionadas