Por que o C# não oferece suporte a tipos genéricos implícitos em construtores de classes?

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

  •  09-06-2019
  •  | 
  •  

Pergunta

C# não exige que você especifique um parâmetro de tipo genérico se o compilador puder inferi-lo, por exemplo:

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

Isso é necessário para tipos anônimos onde você não sabe qual seria o parâmetro de tipo (no intellisense ele aparece como 'a) porque é adicionado pelo compilador.

Os parâmetros de tipo no nível da classe não permitem fazer isso:

//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 que o C# não oferece suporte a essa inferência de tipo genérico em nível de classe?

Foi útil?

Solução

Na verdade, sua pergunta não é ruim.Tenho brincado com uma linguagem de programação genérica nos últimos anos e, embora nunca tenha conseguido desenvolvê-la (e provavelmente nunca o farei), pensei muito sobre inferência de tipos genéricos e uma das minhas principais prioridades tem sempre foi permitir a construção de classes sem a necessidade de especificar o tipo genérico.

C# simplesmente não possui o conjunto de regras para tornar isso possível.Acho que os desenvolvedores nunca viram a necessidade de incluir isso.Na verdade, o código a seguir estaria muito próximo da sua proposta e resolveria o problema.Tudo o que o C# precisa é de um suporte de sintaxe 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);

Como esse código realmente funciona, mostramos que o problema não é de semântica, mas simplesmente de falta de suporte.Acho que tenho que retirar minha postagem anterior.;-)

Outras dicas

Por que o C# não oferece suporte a essa inferência de tipo genérico em nível de classe?

Porque geralmente são ambíguos.Por outro lado, a inferência de tipos é trivial para chamadas de função (se todos os tipos aparecerem nos argumentos).Mas no caso de chamadas de construtor (funções glorificadas, para fins de discussão), o compilador precisa resolver vários níveis ao mesmo tempo.Um nível é o nível da classe e o outro é o nível dos argumentos do construtor.Acredito que resolver isso não é algoritmicamente trivial.Intuitivamente, eu diria que é até NP-completo.

Para ilustrar um caso extremo onde a resolução é impossível, imagine a seguinte classe e diga-me o que o compilador deve fazer:

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

var x = new Foo(1);

Obrigado Konrad, é uma boa resposta (+1), mas apenas para expandir.

Vamos fingir que C# tem uma função construtora 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

Você está certo ao dizer que o primeiro construtor não pode ser inferido.

Agora vamos voltar para a aula

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

Claro, se eu adicionar seu construtor novamente (com seu tipo alternativo), teremos uma chamada ambígua - exatamente como se uma sobrecarga normal de método não pudesse ser resolvida.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top