TransWikia.com

Para que serve esse 'in' em C#?

Stack Overflow em Português Asked by CypherPotato on December 14, 2021

Na nova versão do C#, versão 7.3 foi introduzida o alterador de parâmetros in, mas não entendi sua funcionalidade.

Pelo nome, parece que é usado como “entrada” para os valores, contrário que o out faz. Eu sei que o out faz o seguinte:

string variavelNulaNaDeclaracao = null;
Alterar(out variavelNulaNaDeclaracao);

void Alterar(out string data) => data = "Olá, mundo!";

então, variavelNulaNaDeclaracao será “Olá mundo” mesmo sendo nula na declaração. Mas e quanto ao in? Só entendi que ele também serve para alterar a assinatura dos métodos e criar um overload entre eles, fora isso, não achei outras utilidades.

void Funcao(int x, int y) { ... }
void Funcao(in int x, in int y) { ... }

Para que serve esse in? Para passar apenas o valor duma variável? Ou a referência inteira? Ele irá descartar o uso do ref?

One Answer

Sabe para que serve o ref e sabe para que serve o readonly? Pois bem, é uma junção dos dois.

Portanto está dizendo que vai passar um valor como referência, em vez de copiar o valor para outra variável, então o que é copiado é o endereço de onde está o valor (isso é uma referência), e este parâmetro (que não deixa de ser uma variável) será somente de leitura, o que quer dizer que você não pode mudar o valor referenciado, então é um parâmetro que garante um contrato (promete) que o valor será imutável ali dentro.

Ao contrário de out e ref que só se relacionam com variáveis como argumento, o in aceita literais ou expressões, porque ele só precisa do valor, o resultado não será armazenado.

Veja mais sobre tipos por valor e referência em Alocação de memória em C# - Tipos valor e tipos referência e Qual a diferença entre Struct e Class?.

Qual a diferença mais fundamental entre uma classe e uma estrutura? A primeira é sempre por referência e a segunda é sempre por valor. Em C#, no momento, também ocorre que classes sempre estão no heap (mas isso pode não ser verdade absoluta no futuro, ainda que com restrições), estruturas podem estar em vários lugares, mas se forem locais estarão sempre na pilha (salvo uma otimização que a coloque no registrador do processador ou um upvalue por causa de uma closure ou algo assim, que coloque no heap circunstancialmente).

Qual é o grande problemas das classes? Ela aloca no heap e está sujeita à coletas de lixo. Então ela ajuda a lotar o gerenciador de memória e causa pausas e processamentos extras do GC. Devemos tentar evitar que o garbage collector trabalhe muito para ser bem eficiente. Então melhor não usá-las, certo? O problema é que alguns valores não funcionam bem como tipos por valor por uma série de razões que acho que não vem ao caso aqui, mas na maior parte já está respondida nas perguntas linkadas acima. Como ter um tipo por referência sem alocar no heap e prejudicar o GC? O C# 7.x é sobre isto, especialmente o 7.2.

Ele criou o ref struct onde obriga o tipo ser por referência, mas mantém a capacidade de estar na stack. Só que é comum que tipos assim sejam imutáveis, então existe o ref readonly struct, ou até mesmo o readonly struct se só quiser garantir a imutabilidade da estrutura normal.

Pode criar uma struct com características de uma class e usar como se fosse uma, mas alocando na stack, e de forma segura para a intenção. Este site aqui usa técnicas assim, mas é complicado fazer certo, tem que tomar cuidado e não é recomendado para quem não é muito avançado. Ficou mais fácil com o 7.2 (não é do 7.3), embora ainda exija entendimento do que está fazendo.

Acontece que nem sempre você tem isso pronto, não faz sentido, você pode querer usar um in que originalmente é uma struct sem ser ref.

Muitas vezes se usava o ref para isso. O problema é que você poderia mudar o valor, até mesmo inadvertidamente. E a ideia do ref na verdade é alterar o seu valor (o que era mais necessário quando não havia tuplas), você usa quando precisa disto. Assim como o out que é útil para retornar um valor pelo argumento, e que é melhor porque é otimizado, vai em apenas uma direção, inclusive há uma garantia que não vai um valor na entrada.

O in então permite passar por referência (é chamado de aliasing) garantindo que o valor não muda, por isso não tem volta, pode ser otimizado, seja passando uma ref readonly struct criada assim ou se for só um dos dois modificadores, ou sem nenhum deles. Em alguns casos você evita alocação também, principalmente em casos onde a estrutura precisa ter uma semântica mais próxima de uma classe, ou principalmente com tamanho maior que geralmente uma estrutura tem, mas foi criada como estrutura por razões de otimização.

A mudança no 7.3 é o parâmetro com in que passou ser diferente do parâmetro sem ele, então são assinaturas diferentes. Mas o 7.2 agora tem isto também porque foi considerado que o comportamento original era um bug.

O return passou ter algo semelhante.

O in em tipos por referência ou por valor, mas simples, como o int, é considerado como irrelevante ou inútil, já que trocará seis por meia dúzia. O ganho se dará em cima de tipos por valor com mais de 16 bytes, e pela minha experiência, na maioria das plataformas atuais, o ganho só será expressivo em objetos com mais de 64 bytes (acho que é pelo tamanho do cache line e a forma como o backend do compilador otimiza para algumas plataformas).

Answered by Maniero on December 14, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP