Toon posts:

[C#] Inferred generic type parameters

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
In de volgende methode specificeer ik een zgn. generic type parameter U. De constraint is dat U van type T moet zijn, of een subtype daarvan.

C#:
1
2
// Onderdeel van de interface IRepository<T>
IQueryable<U> Find<U>() where U : T;


Ik kan deze methode echter alleen zo aanroepen:

C#:
1
repository.Find<Project>();


In dit voorbeeld is repository een instantie van ProjectRepository welke IRepository<Project> implementeert. Dus je zou denken dat de generieke parameter voor de Find methode overbodig is tenzij je een subtype wil specificeren. Immers, de repository is een implementatie waarbij T bekend is (Project), dus tenzij anders gespecificeerd, zou U toch ook van type Project moeten zijn (dat zou aangenomen kunnen worden).

C#:
1
repository.Find();


Werkt niet: "The type arguments cannot be inferred from the usage".

Waarom is dat?

Acties:
  • 0 Henk 'm!

  • Casteloni
  • Registratie: November 2001
  • Laatst online: 13-09 11:07
Verwijderd schreef op woensdag 28 januari 2009 @ 11:57:
Immers, de repository is een implementatie waarbij T bekend is (Project), dus tenzij anders gespecificeerd, zou U toch ook van type Project moeten zijn (dat zou aangenomen kunnen worden).
En volgens mij is dat precies wat de compiler wilt controleren? Of U dus daadwerkelijk een subtype is van T?

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Ik bedoel, als ik niets bijzonders specificeer, dan zou aangenomen kunnen worden dat U van type T is, en geen subtype. En T zou duidelijk kunnen zijn doordat de class IRepository<T> implementeert en daarbij voor de class Project specificeert voor T. Lijkt mij dat de compiler alle informatie heeft om te cocluderen dat ik Find<Project>() bedoel, en niet Find<ProjectSubType>(), want anders had ik dat wel gezegd.

Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-09 16:37

.oisyn

Moderator Devschuur®

Demotivational Speaker

Verwijderd schreef op woensdag 28 januari 2009 @ 11:57:
In dit voorbeeld is repository een instantie van ProjectRepository welke IRepository<Project> implementeert.
De fout die je maakt is dat je denkt dat dat er iets mee te maken heeft. Dat is niet zo. Het type van repository is compleet ongerelateerd aan de generic parameter van de Find() method die door de repository geïmplementeerd wordt.
Sorry, ik begreep je verkeerd.
Verwijderd schreef op woensdag 28 januari 2009 @ 12:08:
Ik bedoel, als ik niets bijzonders specificeer, dan zou aangenomen kunnen worden dat U van type T is, en geen subtype.
Dat had zo kunnen zijn ja. Maar daar hebben ze blijkbaar niet voor gekozen :)

[ Voor 27% gewijzigd door .oisyn op 28-01-2009 12:24 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Kan C# generic types bepalen aan de hand van return types? (ik leer sinds 2 weken c# en asp.net, dus ik kan niets met zekerheid zeggen, maar het lijkt me vanuit C++ template oogpunt logisch dat dit niet werkt; je geeft immers nergens aan wat het type zou moeten zijn. Waar moet de compiler die info vandaan toveren? Zelfs als je aan een instance van een class die IQueryable implementeert assigned vraag ik me af of hij dat kan bepalen, tenzij je expliciet cast naar IQueryable... en dan nog, maar misschien kan C# dat?)

[ Voor 80% gewijzigd door Zoijar op 28-01-2009 12:21 ]


Acties:
  • 0 Henk 'm!

  • king_charles
  • Registratie: Maart 2008
  • Laatst online: 15-08-2023
Zoijar schreef op woensdag 28 januari 2009 @ 12:16:
Kan C# generic types bepalen aan de hand van return types?
Dit heeft niks met de vraag in dit topic te maken. Het lijkt me dat je zoiets wel op google kunt vinden en anders kun je beter een apart topic openen.

Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Verwijderd schreef op woensdag 28 januari 2009 @ 11:57:
In de volgende methode specificeer ik een zgn. generic type parameter U. De constraint is dat U van type T moet zijn, of een subtype daarvan.

C#:
1
2
// Onderdeel van de interface IRepository<T>
IQueryable<U> Find<U>() where U : T;


Ik kan deze methode echter alleen zo aanroepen:

C#:
1
repository.Find<Project>();


In dit voorbeeld is repository een instantie van ProjectRepository welke IRepository<Project> implementeert. Dus je zou denken dat de generieke parameter voor de Find methode overbodig is tenzij je een subtype wil specificeren. Immers, de repository is een implementatie waarbij T bekend is (Project), dus tenzij anders gespecificeerd, zou U toch ook van type Project moeten zijn (dat zou aangenomen kunnen worden).

C#:
1
repository.Find();


Werkt niet: "The type arguments cannot be inferred from the usage".

Waarom is dat?
Wat je eigenlijk wil is dus dat een Generic type parameter automatisch evalueert naar het meest generieke type wat voldoet aan de Constraints voor dat type.

Op zich zou dat mogenlijk zijn, maar dat zou ook inhouden dat als je bij een Generic method geen constraint hebt dit zou moeten evalueren naar Object, ook zijn er situaties te bedenken waar het uberhaupt niet mogenlijk is.

C#:
1
2
3
4
5
6
interface IFoo {}
interface IBar {}

class Bar<T> where T : IFoo, IBar
{
}


Natuurlijk zou je pas op dat moment een compiler error kunnen geven, maar dan word het wel weer erg inconsistent. Ze hebben er denk voor gekozen omdat het gewoon netjes is om expliciet aan te geven welke Type Parameter gebruikt word.

[ Voor 3% gewijzigd door Woy op 28-01-2009 12:48 ]

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Oh, ik dacht dat hij een method Find() had die een generic object returned. "IQueryable<U> Find<U>() where U : T;" betekent toch gewoon een method Find zonder parameters die een een generic IQueryable returned met instantie van type U dat een subtype van T is?

Acties:
  • 0 Henk 'm!

  • Niemand_Anders
  • Registratie: Juli 2006
  • Laatst online: 09-07-2024

Niemand_Anders

Dat was ik niet..

Je kunt toch gewoon een overload gebruiken zodat je een Find() en een Find<U>() hebt? Indien je heeft generic type meegeeft aan de methode, dan IQueryable van het type T zijn, anders van het type U..

If it isn't broken, fix it until it is..


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Zoijar schreef op woensdag 28 januari 2009 @ 12:46:
Oh, ik dacht dat hij een method Find() had die een generic object returned. "IQueryable<U> Find<U>() where U : T;" betekent toch gewoon een method Find zonder parameters die een een generic IQueryable returned met instantie van type U dat een subtype van T is?
Ja dat klopt, maar je moet wel expliciet opgeven van welk type U is. Als je method parameters heeft die ook Generic zijn, dan kunnen de Type parameters meestal Inferred worden zodat je ze niet meer hoeft te typen, maar in dit geval is dat niet zo. Wat de TS zou willen is dat het dan automatisch naar "Project" evalueert ( of ieder geval het type waardoor de constraint voldaan word )
Niemand_Anders schreef op woensdag 28 januari 2009 @ 12:48:
Je kunt toch gewoon een overload gebruiken zodat je een Find() en een Find<U>() hebt? Indien je heeft generic type meegeeft aan de methode, dan IQueryable van het type T zijn, anders van het type U..
Idd dan kan je gewoon zo doen
C#:
1
2
IQueryable<U> Find<U>() where U : T;
IQueryable<T> Find() { return Find<T>(); }

[ Voor 55% gewijzigd door Woy op 28-01-2009 12:54 ]

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

rwb schreef op woensdag 28 januari 2009 @ 12:50:
Ja dat klopt, maar je moet wel expliciet opgeven van welk type U is. Als je method parameters heeft die ook Generic zijn, dan kunnen de Type parameters meestal Inferred worden zodat je ze niet meer hoeft te typen, maar in dit geval is dat niet zo. Wat de TS zou willen is dat het dan automatisch naar "Project" evalueert ( of ieder geval het type waardoor de constraint voldaan word )
Ok, precies wat ik eerder al zei dus waarom het niet werkt (overigens zonder een oplossing te geven, agreed), waarop ik vriendelijk naar google werd verwezen ;)

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Niemand_Anders schreef op woensdag 28 januari 2009 @ 12:48:
Je kunt toch gewoon een overload gebruiken zodat je een Find() en een Find<U>() hebt? Indien je heeft generic type meegeeft aan de methode, dan IQueryable van het type T zijn, anders van het type U..
Yep, maar dan loop ik tegen een bug aan in .NET op het moment dat ik... lastig verhaal... m.b.v. Moq dynamische implementaties van de repositories implementeer t.b.v. unit tests. De code compileert, maar is daarna niet verifieerbaar. Het resultaat is een System.TypeLoadException.

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
rwb schreef op woensdag 28 januari 2009 @ 12:46:
[...]
Op zich zou dat mogenlijk zijn, maar dat zou ook inhouden dat als je bij een Generic method geen constraint hebt dit zou moeten evalueren naar Object, ook zijn er situaties te bedenken waar het uberhaupt niet mogenlijk is.

C#:
1
2
3
4
5
6
interface IFoo {}
interface IBar {}

class Bar<T> where T : IFoo, IBar
{
}


Natuurlijk zou je pas op dat moment een compiler error kunnen geven, maar dan word het wel weer erg inconsistent. Ze hebben er denk voor gekozen omdat het gewoon netjes is om expliciet aan te geven welke Type Parameter gebruikt word.
Ja, niet aan gedacht inderdaad. Is wel vaker dat ze liever kiezen voor consistentie in de C# compiler. Misschien ook wel beter...

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
rwb schreef op woensdag 28 januari 2009 @ 12:50:
Idd dan kan je gewoon zo doen
C#:
1
2
IQueryable<U> Find<U>() where U : T;
IQueryable<T> Find() { return Find<T>(); }
Niet in een interface helaas...

Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Nee de implementatie kun je niet in een interface zetten, maar de definitie wel. Je zou eventueel een abstracte base-class kunnen maken die de implementatie verzorgt, of de implementatie gewoon aan de implenterende class over te laten.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
rwb schreef op woensdag 28 januari 2009 @ 14:14:
Nee de implementatie kun je niet in een interface zetten, maar de definitie wel.
Ja, helaas loop ik dan tegen een andere bug aan, zo'n interface kan ik niet mocken met Moq in m'n unit tests (bug) :-(

Acties:
  • 0 Henk 'm!

  • EfBe
  • Registratie: Januari 2000
  • Niet online
Type inferring vindt plaats op basis van input parameters. Jij wilt dat laten plaatsvinden mbv de returntype, maar dat gaat niet, want de implementatie van de interface vertelt niets over hoe het intern wordt geimplementeerd.

Dus expliciet specificeren, want dan kan de compiler 'U' bepalen voor de call die je op dat moment maakt.

Maak overigens geen zooi van generic interfaces met restricties want je zit zo hopeloos vast in de restricties die de interfaces aan de implementatie opleggen. Interfaces zijn een andere manier van generic programming en hebben veelal geen generic parameter nodig.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
EfBe schreef op woensdag 28 januari 2009 @ 15:12:
Maak overigens geen zooi van generic interfaces met restricties want je zit zo hopeloos vast in de restricties die de interfaces aan de implementatie opleggen. Interfaces zijn een andere manier van generic programming en hebben veelal geen generic parameter nodig.
C#:
1
2
3
4
5
public class VehicleRepository : IRepository<Vehicle> {}

var repository = new VehicleRepository();
var cars = repository.FindAll<Car>();
var bikes = repository.FindAll<Bike>();


Goed idee? Of niet? :?

Bedankt!

Acties:
  • 0 Henk 'm!

  • EfBe
  • Registratie: Januari 2000
  • Niet online
Verwijderd schreef op woensdag 28 januari 2009 @ 20:08:
[...]

C#:
1
2
3
4
5
public class VehicleRepository : IRepository<Vehicle> {}

var repository = new VehicleRepository();
var cars = repository.FindAll<Car>();
var bikes = repository.FindAll<Bike>();


Goed idee? Of niet? :?

Bedankt!
Wat doet die interface 'IRepository' ? Juist, die geeft een specifiek type (VehicleRepository) een generic type zodat je iedere repository middels 'IRepository' kunt gebruiken in generieke code. Multiple type inheritance dus.

'Vehicle' in de IRepository interface is echter een roadblock: Jij kunt niet een random repository object aanspreken zonder dat jij dat generic type weet.

Ik las in een eerdere post dat je mocking gebruikt: wees wel beducht op het feit dat interfaces alleen nut hebben indien je ze gebruikt voor multiple type inheritance. Gebruik je dat niet, dan ben je je code eigenlijk complexer aan het maken dan het feitelijk is: een class Foo die als enige IFoo implementeert is onzin, 'IFoo' voegt nl. dan niets toe.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com

Pagina: 1