Pergunta

Leve o seguinte:

class A {}

class B : A {}

class C
{
    C()
    {
        var b = new B();
        Foo(b);
        Foo2(ref b); // <= compile-time error: 
                     // "The 'ref' argument doesn't match the parameter type"
    }

    void Foo(A a) {}

    void Foo2(ref A a) {}  
}

Por que ocorre o erro de tempo de compilação acima? Isso acontece com ambos os argumentos ref e out.

Foi útil?

Solução

=============

UPDATE: eu usei esta resposta como a base para esta entrada do blog:

Por que parâmetros ref e fora não permite alteração de tipo?

Veja a página de blog para mais comentários sobre esta questão. Agradecimentos para a grande questão.

=============

Vamos supor que você tem aulas Animal, Mammal, Reptile, Giraffe, Turtle e Tiger, com as relações de subclassificação óbvio.

Agora, suponha que você tem um void M(ref Mammal m) método. M pode tanto ler e escrever m.


Você pode passar uma variável do tipo Animal para M?

No. Essa variável pode conter um Turtle, mas M vai assumir que ele contém apenas mamíferos. A Turtle não é um Mammal.

Conclusão 1 : parâmetros ref não pode ser feita "maior". (Há mais animais do que os mamíferos, então a variável está ficando "maior" porque pode conter mais coisas.)


Você pode passar uma variável do tipo Giraffe para M?

No. M pode escrever para m e M pode querer escrever uma Tiger em m. Agora você colocar um Tiger em uma variável que é realmente do tipo Giraffe.

Conclusão 2 :. Parâmetros ref não pode ser feita "menor"


Agora, considere N(out Mammal n).

Você pode passar uma variável do tipo Giraffe para N?

No. N pode escrever para n e N pode querer escrever uma Tiger.

Conclusão 3 :. Parâmetros out não pode ser feita "menor"


Você pode passar uma variável do tipo Animal para N?

Hmm.

Bem, por que não? N não pode ler n, só pode escrever para ele, certo? Você escreve um Tiger a uma variável do tipo Animal e está tudo pronto, certo?

errada. A regra não é "N só pode escrever para n".

As regras são, resumidamente:

1) N tem de escrever para n antes N retorna normalmente. (Se N mantas, todas as apostas estão fora.)

2) N tem que escrever algo para n antes ele lê algo de n.

Isso permite que essa seqüência de eventos:

  • Declare uma x campo do tipo Animal.
  • Passe x como um parâmetro out para N.
  • N escreve um Tiger em n, que é um alias para x.
  • Em outro segmento, alguém escreve um Turtle em x.
  • N tentativas de ler o conteúdo do n, e descobre um Turtle no que ele pensa que é uma variável do tipo Mammal.

É claro que nós queremos fazer é ilegal.

Conclusão 4 :. Parâmetros out não pode ser feita "maior"


Conclusão final : Nem os parâmetros ref nem out podem variar seus tipos. Para fazer o contrário é quebrar a segurança de tipos verificável.

Se estas questões no tipo theo básicary lhe interessam, considere ler minha série sobre como covariância e contravariance trabalho em C # 4.0 .

Outras dicas

Porque em ambos os casos, você deve ser capaz de valor atribuir a ref / out parâmetro.

Se você tentar passar b em método foo2 como referência, e em foo2 tentar assing a = new A (), isso seria inválido.
Mesma razão que você não pode escrever:

B b = new A();

Considere o seguinte:

class C : A {}
class B : A {}

void Foo2(ref A a) { a = new C(); } 

B b = null;
Foo2(ref b);

É violaria tipo de segurança

Enquanto as outras respostas têm sucintamente explicou o raciocínio por trás esse comportamento, eu acho que vale a pena mencionar que, se você realmente precisa fazer algo desta natureza você pode realizar funcionalidade semelhante, fazendo foo2 em um método genérico, como tal:

class A {}

class B : A {}

class C
{
    C()
    {
        var b = new B();
        Foo(b);
        Foo2(ref b); // <= no compile error!
    }

    void Foo(A a) {}

    void Foo2<AType> (ref AType a) where AType: A {}  
}

Porque dar Foo2 um ref B resultaria em um objeto malformado porque Foo2 só sabe como preencher parte A de B.

Não é que o compilador lhe dizendo que gostaria que você converter explicitamente o objeto para que ele pode ter certeza que você sabe quais são suas intenções?

Foo2(ref (A)b)

Faz sentido a partir de uma perspectiva de segurança, mas eu teria preferido que se o compilador deu um aviso, em vez de um erro, uma vez que existem usos legítimos de objetos polymoprhic passados ??por referência. por exemplo.

class Derp : interfaceX
{
   int somevalue=0; //specified that this class contains somevalue by interfaceX
   public Derp(int val)
    {
    somevalue = val;
    }

}


void Foo(ref object obj){
    int result = (interfaceX)obj.somevalue;
    //do stuff to result variable... in my case data access
    obj = Activator.CreateInstance(obj.GetType(), result);
}

main()
{
   Derp x = new Derp();
   Foo(ref Derp);
}

Isso não irá compilar, mas será que funciona?

No meu caso a minha função aceita um objeto e eu não poderia enviar em nada, então eu simplesmente fiz

object bla = myVar;
Foo(ref bla);

E que as obras

Meu Foo está em VB.NET e ele verifica tipo dentro e faz um monte de lógica

Peço desculpas se minha resposta é duplicado, mas outros eram demasiado longo

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