Pergunta

Eu tenho uma classe que tem um tipo genérico "G"

No meu modelo de classe eu tenho

public class DetailElement : ElementDefinition

Vamos dizer que eu tenho um método como este

        public void DoSomething<G>(G generic)
            where G : ElementDefinition
        {
            if (generic is DetailElement)
            {
                ((DetailElement)generic).DescEN = "Hello people"; //line 1
                //////
                ElementDefinition element = generic;
                ((DetailElement)element).DescEN = "Hello again"; //line 3
                //////
                (generic as DetailElement).DescEN = "Howdy"; //line 5
            }
            else
            {
                //do other stuff
            }
        }

Compiler relata um erro na linha 1:

Cannot convert type 'G' to 'DetailElement'

Mas a linha 3 funciona bem. Eu posso solucionar esse problema fazendo o código escrito na linha 5.

O que eu gostaria de saber é por que o compilador informa o erro na linha 1 e não a um na linha 3, dado que, tanto quanto eu sei, eles são idênticos.

edit: estou com medo de que eu poderia estar faltando alguma peça importante da lógica de quadro

edit2: Embora soluções para o erro do compilador são importantes, a minha pergunta é sobre porque o compilador relata um erro na linha 1 e não na linha 3

.
Foi útil?

Solução

Se G foi constrangido a ser um DetailElement (where G : DetailElement), então você pode ir em frente e G elenco para ElementDefinition, ou seja, "(ElementDefinition) generic". Mas porque G poderia ser outra subclasse de ElementDefinition diferente DetailElement em tempo de execução que não vai permitir que ele em tempo de compilação em que o tipo é desconhecido e não verificável.

Na linha 3 do tipo que você fundido a partir de é conhecido por ser um ElementDefinition então tudo que você está fazendo é um up-cast . O compilador não sei se ele vai ser um elenco succcesful em tempo de execução, mas ele vai confiar em você lá. O compilador não está tão confiante para os genéricos.

O as operador na linha 5 também pode retornar nulo eo compilador não verifica estaticamente o tipo para ver se é seguro nesse caso. Você pode usar as com qualquer tipo, e não apenas aqueles que são compatíveis com ElementDefinition.

De posso fundido de e para o tipo genérico parâmetros no MSDN:?

O compilador só vai deixar você lançar implicitamente parâmetros de tipo genérico de objeto, ou para tipos especificados-restrição.

Tal conversão implícita é do tipo caminho seguro, pois qualquer incompatibilidade é descoberto em tempo de compilação.

O compilador vai deixar você converter explicitamente parâmetros de tipo genérico para qualquer interface, mas não para uma classe:

   interface ISomeInterface {...}
   class SomeClass {...}
   class MyClass<T> 
    {
      void SomeMethod(T t)
       {
         ISomeInterface obj1 = (ISomeInterface)t;//Compiles
         SomeClass      obj2 = (SomeClass)t;     //Does not compile
       }
    }

No entanto, você pode forçar um elenco de um parâmetro de tipo genérico para qualquer outro tipo usando uma variável objeto temporário

 void SomeMethod<T>(T t) 
  { object temp = t;
    MyOtherClass obj = (MyOtherClass)temp;  
  }

É desnecessário dizer que tal conversão explícita é perigoso porque pode lançar uma exceção em tempo de execução se o tipo de concreto usado em vez do parâmetro de tipo genérico não derivam do tipo que você explicitamente convertido para.

Em vez de arriscar uma exceção casting, uma abordagem melhor é usar os operadores is ou as. O operador is retorna true se o parâmetro de tipo genérico é do tipo consultado e as irá realizar um elenco se os tipos são compatíveis, e retornará null contrário.

public void SomeMethod(T t)
 {
   if(t is int) {...}

   string str = t as string;
   if(str != null) {...}
 }

Outras dicas

Geralmente, upcasting é um cheiro de código. Você pode evitá-lo por sobrecarga de método. Tente isto:

public void DoSomething(DetailElement detailElement)
{
    // do DetailElement specific stuff
}

public void DoSomething<G>(G elementDefinition)
    where G : ElementDefinition
{
    // do generic ElementDefinition stuff
}

Você pode então tirar proveito de sobrecarga de método usando este código:

DetailElement foo = new DetailElement();

DoSomething(foo); // calls the non-generic method
DoSomething((ElementDefinition) foo); // calls the generic method

não deve sua cláusula where ser "onde G: DetailElement"?

No código que você escreveu, um DetailElement é um ElementDefinition, mas um ElementDefinition não é necessariamente uma DetailElement. Assim, a conversão implícita é ilegal.

Existem outros tipos de ElementDefinition que você pode passar para este método? Se assim for, eles vão lançar uma exceção quando você tenta lançá-los em instâncias DetailElement.

EDIT:

Ok, então agora que você mudou sua listagem de código, eu posso ver que você está verificando o tipo para se certificar de que realmente é um DetailElement antes de entrar nesse bloco de código. Infelizmente, o fato da questão é que você pode não implicitamente baixos, mesmo se você já verificou os tipos si mesmo. Eu acho que você realmente deveria usar o "como" palavra-chave no início do seu bloco:

DetailElement detail = generic as DetailElement;
if (detail == null) {
   // process other types of ElementDefinition
} else {
   // process DetailElement objects
}

Melhor ainda, por que não usar polimorfismo para permitir que cada tipo de ElementDefinition para definir seu próprio método DoSomething, e deixar que o CLR cuidar do tipo de verificação e invocação de método para você?

Isto levará a um pouco mais de código se você tem um monte de ElementDefinitions você está preocupado com, mas é provavelmente o mais liso você terá que não envolve é, então, como um disparate.

    public void DoSomething<G>(G generic)
        where G : ElementDefinition
    {
        DetailElement detail = generic as DetailElement;
        if (detail != null)
        {
            detail.DescEN = "Hello people";
        }
        else
        {
            //do other stuff
        }
    }

Outra possível solução que eu usei quando eu precisava de tais informações, em loo de uma variável objeto temporário.

DetailElement detail = (DetailElement)(object)generic;

Ele funciona, mas o que forma é provavelmente o melhor.

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