Por que existe uma nova restrição () em C #, mas nenhuma outra restrição semelhante?


19

Em C # genéricos, podemos declarar uma restrição para um parâmetro de tipo Tter um construtor padrão, dizendo where T : new(). No entanto, nenhum outro tipo de restrição como esse é válido - new(string)por exemplo, etc.

Do ponto de vista do design e / ou implementação da linguagem, qual o motivo disso?

Existe algo na maneira como os construtores trabalham ou na maneira como o sistema de tipos é implementado que proíbe isso (ou pelo menos dificulta)? Se assim for, o que é? Lembro-me de ler em algum lugar que default(T)realmente compila new T()para T : struct. Está relacionado a isso, talvez?

Ou é simplesmente uma decisão de design feita para evitar tornar a linguagem muito complicada?


new(string)não é uma restrição de construtor padrão. Sua pergunta equivale a dizer "Por que não existem restrições que exigem assinaturas específicas de construtores?" Muito provavelmente porque tal restrição não seria particularmente útil.
22714 Robert Harvey

3
@RobertHarvey Sim, é exatamente o que estou dizendo. Estou essencialmente perguntando se existe algo que torna os construtores padrão especiais em termos de implementação ou se foi apenas uma escolha arbitrária incluir isso e não o outro.
Theodoros Chatzigiannakis

Construtores padrão são úteis de certas maneiras específicas e importantes. Por exemplo, eles tornam um tipo prontamente serializável.
Robert Harvey

6
Assinaturas de ctor específicas podem ser úteis. Por exemplo, se a sua variável de tipo for restrita a ser da Coleção <T> com um ctor de T (Coleção <T>), você sabe que pode construir novas coleções dadas outra. Se essa utilidade vale a complexidade extra, no entanto, é uma questão.
Phoshi

Respostas:


5

Para uma resposta autorizada, remeto-lhe a resposta de Eric Lippert para essa pergunta no StackOverflow há alguns anos atrás, um trecho da qual é brevemente citado abaixo.

No entanto, neste caso específico, certamente posso lhe dar algumas razões pelas quais eu recuaria no recurso se ele surgisse em uma reunião de design como um recurso possível para uma versão futura do idioma.

...

Eu digo que devemos fazer todo o recurso ou não fazê-lo. Se é importante poder restringir tipos para ter construtores específicos, vamos fazer o recurso inteiro e restringir tipos com base em membros em geral e não apenas em construtores.


2
Essa citação, tirada de contexto, é muito enganadora. Isso sugere que Eric pensou que a Microsoft deveria ter "ido até o fim", o que não é a sua posição. Ele é, de fato, totalmente contra o recurso proposto.
22414 Robert Harvey

1
Obrigado, isso responde parcialmente à minha pergunta: perto do final de sua resposta, Eric Lippert menciona que é uma limitação da IL e a inclusão desse recurso exigiria uma adição à IL. Seria perfeito se você pudesse fornecer uma fonte sobre qual IL é gerada para a parte que é implementada - ou seja, chamar genericamente um construtor padrão.
Theodoros Chatzigiannakis

@TheodorosChatzigiannakis: Por que você não aciona o Decompiler da Telerik e descobre por si mesmo?
22414 Robert Harvey

2
@RobertHarvey Não acho que isso sugira uma posição para ele, mas incluí uma citação adicional para enfatizar sua posição.
22614 Chris Hannon

1
Hmm, o F # tem maneiras mais avançadas de restringir um tipo , como verificar como tempo de compilação se uma classe tem um operador. Talvez o F # precise de um sistema de restrição mais poderoso que o C # devido à sua verificação de tipo muito rigorosa. No entanto, essa linguagem é capaz de implementar maneiras avançadas de manter a classe no .Net Framework.
usar o seguinte código

16

Descompilar (conforme sugestão de Robert Harvey) produziu o seguinte, para qualquer pessoa interessada. Este método:

static T GenericMake<T>()
    where T : new()
{
    return new T();
}

Aparentemente, quando compilado, torna-se o seguinte:

private static T GenericMake<T>()
    where T : new()
{
    T t;
    T t1 = default(T);
    if (t1 == null)
    {
        t = Activator.CreateInstance<T>();
    }
    else
    {
        t1 = default(T);
        t = t1;
    }
    return t;
}
  • Se Té um tipo de valor, new()torna-se default(T).
  • Se Té um tipo de referência, new()funciona usando reflexão. Activator.CreateInstance()chamadas internamente RuntimeType.CreateInstanceDefaultCtor().

Então aqui está - internamente, os construtores padrão são realmente especiais para C # em relação ao CLR. Dar a outros construtores o mesmo tratamento teria sido caro, mesmo se houver alguns casos de uso válidos para restrições mais complicadas em genéricos.


4
Interessante. Por que a chamada duplicada default(T) para tipos de valor?
Avner Shahar-Kashtan

2
@ AvnerShahar-Kashtan Eu não sei. Pode ser um artefato do processo de compilação / descompilação, como a tvariável (que pode ser substituída por returninstruções aninhadas ).
Theodoros Chatzigiannakis
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.