¿Por qué C# no admite tipos genéricos implícitos en constructores de clases?

StackOverflow https://stackoverflow.com/questions/45604

  •  09-06-2019
  •  | 
  •  

Pregunta

C# no requiere que especifiques un parámetro de tipo genérico si el compilador puede inferirlo, por ejemplo:

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();

Esto es necesario para tipos anónimos donde no se sabe cuál sería el parámetro de tipo (en Intellisense aparece como 'a) porque lo agrega el compilador.

Los parámetros de tipo a nivel de clase no te permiten hacer esto:

//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 );

¿Por qué C# no admite esta inferencia de tipos genéricos a nivel de clase?

¿Fue útil?

Solución

En realidad, tu pregunta no es mala.He estado jugando con un lenguaje de programación genérico durante los últimos años y, aunque nunca he llegado a desarrollarlo (y probablemente nunca lo haré), he pensado mucho en la inferencia de tipos genéricos y una de mis principales prioridades es Siempre ha sido permitir la construcción de clases sin tener que especificar el tipo genérico.

C# simplemente carece del conjunto de reglas para hacer esto posible.Creo que los desarrolladores nunca vieron la necesidad de incluir esto.En realidad, el siguiente código estaría muy cerca de su propuesta y resolvería el problema.Todo lo que C# necesita es soporte de sintaxis adicional.

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);

Dado que este código realmente funciona, hemos demostrado que el problema no es de semántica sino simplemente de falta de soporte.Supongo que tengo que retirar mi publicación anterior.;-)

Otros consejos

¿Por qué C# no admite esta inferencia de tipos genéricos a nivel de clase?

Porque generalmente son ambiguos.Por el contrario, la inferencia de tipos es trivial para llamadas a funciones (si todos los tipos aparecen en los argumentos).Pero en el caso de llamadas al constructor (funciones glorificadas, por el bien de la discusión), el compilador tiene que resolver múltiples niveles al mismo tiempo.Un nivel es el nivel de clase y el otro es el nivel de argumentos del constructor.Creo que resolver esto algorítmicamente no es trivial.Intuitivamente, diría que es incluso NP-completo.

Para ilustrar un caso extremo donde la resolución es imposible, imagine la siguiente clase y dígame qué debería hacer el compilador:

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

var x = new Foo(1);

Gracias Konrad, es una buena respuesta (+1), pero solo para ampliarla.

Supongamos que C# tiene una función constructora explícita:

//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

Tienes toda la razón en que no se puede inferir el primer constructor.

Ahora volvamos a la clase.

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 );

Por supuesto, si vuelvo a agregar su constructor (con su tipo alternativo), tenemos una llamada ambigua, exactamente como si una sobrecarga de método normal no pudiera resolverse.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top