48 votes

Pourquoi C# ne prend-il pas en charge les types génériques implicites sur les constructeurs de classe ?

C# ne vous oblige pas à spécifier un paramètre de type générique si le compilateur peut le déduire, par exemple :

List<int> myInts = new List<int> {0,1,1,
    2,3,5,8,13,21,34,55,89,144,233,377,
    610,987,1597,2584,4181,6765};

//this statement is clunky
List<string> myStrings = myInts.
    Select<int,string>( i => i.ToString() ).
    ToList<string>();

//the type is inferred from the lambda expression
//the compiler knows that it's taking an int and 
//returning a string
List<string> myStrings = myInts.
    Select( i => i.ToString() ).
    ToList();

Ceci est nécessaire pour les types anonymes où vous ne savez pas ce que le paramètre de type serait (dans intellisense il apparaît comme 'a ) car il est ajouté par le compilateur.

Les paramètres de type de niveau classe ne vous permettent pas de faire cela :

//sample generic class
public class GenericDemo<T> 
{
    public GenericDemo ( T value ) 
    {
        GenericTypedProperty = value;
    }

    public T GenericTypedProperty {get; set;}
}

//why can't I do:
int anIntValue = 4181;
var item = new GenericDemo( anIntValue ); //type inference fails

//however I can create a wrapper like this:
public static GenericDemo<T> Create<T> ( T value )
{
    return new GenericDemo<T> ( value );
}

//then this works - type inference on the method compiles
var item = Create( anIntValue );

Pourquoi C# ne prend-il pas en charge cette inférence de type générique au niveau de la classe ?

29voto

Konrad Rudolph Points 231505

En fait, votre question n'est pas mauvaise. Je m'amuse avec un langage de programmation générique depuis quelques années et, bien que je n'aie jamais réussi à le développer (et ne le ferai probablement jamais), j'ai beaucoup réfléchi à l'inférence de type générique et l'une de mes priorités a toujours été de permettre la construction de classes sans avoir à spécifier le type générique.

Le C# ne dispose tout simplement pas de l'ensemble de règles permettant de rendre cela possible. Je pense que les développeurs n'ont jamais vu la nécessité de l'inclure. En fait, le code suivant serait très proche de votre proposition et résoudrait le problème. Tout ce dont C# a besoin, c'est d'un support syntaxique supplémentaire.

class Foo<T> {
    public Foo(T x) { … }
}

// Notice: non-generic class overload. Possible in C#!
class Foo {
    public static Foo<T> ctor<T>(T x) { return new Foo<T>(x); }
}

var x = Foo.ctor(42);

Puisque ce code fonctionne réellement, nous avons montré que le problème n'est pas d'ordre sémantique mais simplement un manque de support. Je suppose que je dois retirer mon message précédent ;-)

11voto

Konrad Rudolph Points 231505

Pourquoi C# ne prend-il pas en charge cette inférence de type générique au niveau de la classe ?

Parce qu'ils sont généralement ambigus. En revanche, l'inférence de type est triviale pour les appels de fonction (si tous les types apparaissent dans les arguments). Mais dans le cas des appels de constructeurs (fonctions glorifiées, pour les besoins de la discussion), le compilateur doit résoudre plusieurs niveaux en même temps. Un niveau est celui de la classe et l'autre est celui des arguments du constructeur. Je pense que la résolution de ce problème est algorithmiquement non triviale. Intuitivement, je dirais même que c'est NP-complet.

Pour illustrer un cas extrême où la résolution est impossible, imaginez la classe suivante et dites-moi ce que le compilateur devrait faire :

class Foo<T> {
    public Foo<U>(U x) { }
}

var x = new Foo(1);

2voto

Keith Points 46288

Merci Konrad, c'est une bonne réponse (+1), mais juste pour développer.

Imaginons que le C# dispose d'une fonction constructeur explicite :

//your example
var x = new Foo( 1 );

//becomes
var x = Foo.ctor( 1 );

//your problem is valid because this would be
var x = Foo<T>.ctor<int>( 1 );
//and T can't be inferred

Vous avez tout à fait raison de dire que le premier constructeur ne peut pas être déduit.

Maintenant, retournons à la classe

class Foo<T> 
{
    //<T> can't mean anything else in this context
    public Foo(T x) { }
}

//this would now throw an exception unless the
//typeparam matches the parameter
var x = Foo<int>.ctor( 1 );

//so why wouldn't this work?
var x = Foo.ctor( 1 );

Bien sûr, si je rajoute votre constructeur (avec son type alternatif), nous avons un appel ambigu - exactement comme si une surcharge de méthode normale ne pouvait pas être résolue.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X