C #: 'is' palavra-chave e verificando Não

287

Esta é uma pergunta boba, mas você pode usar esse código para verificar se algo é de um tipo específico ...

if (child is IContainer) { //....

Existe uma maneira mais elegante de verificar a instância "NOT"?

if (!(child is IContainer)) { //A little ugly... silly, yes I know...

//these don't work :)
if (child !is IContainer) {
if (child isnt IContainer) { 
if (child aint IContainer) { 
if (child isnotafreaking IContainer) { 

Sim, sim ... pergunta boba ....

Como há alguma dúvida sobre a aparência do código, é apenas um retorno simples no início de um método.

public void Update(DocumentPart part) {
    part.Update();
    if (!(DocumentPart is IContainer)) { return; }
    foreach(DocumentPart child in ((IContainer)part).Children) {
       //...etc...
Hugoware
fonte
105
Eu pessoalmente gosto da "criança não está pirando ...". Eu estou votando para ter a palavra-chave colocar em C # 5
Joseph
Estou interessado em saber a situação em que você usaria isso? Como é a parte "else" desse código e você não pode simplesmente inverter o teste? Se o seu código está dizendo "se filho não é um IContainer, em seguida, lança exceções" ou "se filho não é um IContainer, então talvez seja um IFoo, então tentarei isso a seguir", então não existe uma declaração implícita? Provavelmente estou sentindo falta de algo.
613 Martin Peck
1
@ MartinPeck, pode não haver uma cláusula else. Essa é a razão pela qual eu procurei por isso.
18715 Joshua Walsh #
@MartinPeck, aqui está uma amostra: if (!(argument is MapsControlViewModel vm)) { return; }- Eu poderia inverter o if e colocar o resto do método dentro dos colchetes do if, mas depois obteria o código da árvore de Natal, com muitos colchetes no final do método. Isso é muito menos legível.
ANeves
talvez o que precisamos em geral, são ifnotdeclarações
Dave Cousineau

Respostas:

301
if(!(child is IContainer))

é o único operador a ir (não há IsNot operador).

Você pode criar um método de extensão que faça isso:

public static bool IsA<T>(this object obj) {
    return obj is T;
}

e use-o para:

if (!child.IsA<IContainer>())

E você pode acompanhar o seu tema:

public static bool IsNotAFreaking<T>(this object obj) {
    return !(obj is T);
}

if (child.IsNotAFreaking<IContainer>()) { // ...

Atualização (considerando o snippet de código do OP):

Como você está realmente lançando o valor posteriormente, você pode usar as:

public void Update(DocumentPart part) {
    part.Update();
    IContainer containerPart = part as IContainer;
    if(containerPart == null) return;
    foreach(DocumentPart child in containerPart.Children) { // omit the cast.
       //...etc...
Mehrdad Afshari
fonte
1
ck: Eu quis dizer, no sentido de operadores, não há IsNotnada.
Mehrdad Afshari
5
Sim. Estou brincando, caso não seja óbvio.
Mehrdad Afshari
111

Você pode fazer assim:

object a = new StreamWriter("c:\\temp\\test.txt");

if (a is TextReader == false)
{
   Console.WriteLine("failed");
}
cjk
fonte
2
@Frank - sim, a palavra-chave é dá um booleano, que você pode comparar a falsa
cjk
32
@Frank funciona porque istem maior precedência em relação a ==. A única razão pela qual você não pode usar !x is fé que ela tem menos precedência do que !.
Mehrdad Afshari
Eu gosto disso, mas parece não funcionar direito ao introduzir uma variável, mesmo que deva. if (a is TextReader reader == false)"deveria" funcionar, mas não permitirá que você use a variável no caminho verdadeiro, dizendo que ela pode não ter sido inicializada.
Dave Cousineau 11/11
@DaveCousineau - Normalmente, você deve verificar e introduzir uma variável quando quiser usar a variável introduzida. Não tenho certeza de como a variável seria útil se a verificação de tipo falhasse. (Aviso - eu acho o recurso "Correspondência de padrão" mal nomeado e com um cheiro de código tão ruim quanto o uso de outparâmetros))
StingyJack
@StingyJack existe algum tipo de falha em que, no caminho verdadeiro , a variável é considerada não inicializada. mesmo se você disser if (a is TextReader reader == true)que a variável não foi inicializada.
Dave Cousineau
11

Por que não usar apenas o resto?

if (child is IContainer)
{
  //
}
else
{
  // Do what you want here
}

É puro familiar e simples?

Mark Broadhurst
fonte
3
Não há nada de errado com isso - esta é apenas uma pergunta detalhada. Eu queria sair imediatamente de uma função se algo não fosse um tipo específico. Eu fiz isso (! (Criança é algo)) para sempre agora, mas pensei em garantir que não houvesse uma maneira melhor.
Hugoware
1
Com o código de exemplo na pergunta, isso significaria um colchete vazio se. Isso não soa como uma alternativa sensata.
ANeves
9

O modo como você o faz é bom, mas você pode criar um conjunto de métodos de extensão para tornar "uma maneira mais elegante de verificar a instância 'NOT'".

public static bool Is<T>(this object myObject)
{
    return (myObject is T);
}

public static bool IsNot<T>(this object myObject)
{
    return !(myObject is T);
}

Então você pode escrever:

if (child.IsNot<IContainer>())
{
    // child is not an IContainer
}
Robert Cartaino
fonte
7

Isso ainda não foi mencionado. Funciona e acho melhor do que usar!(child is IContainer)

if (part is IContainer is false)
{
    return;
}

issintaxe expr is constant :, em que expr é a expressão a ser avaliada e constante é o valor a ser testado.

Todd Skelton
fonte
3
Da mesma forma que você poderia fazer if (part as IContainer is null). Honestamente, não sei o que é melhor.
Flynn1179
5

Feio? Discordo. A única outra maneira (eu pessoalmente acho que isso é "mais feio"):

var obj = child as IContainer;
if(obj == null)
{
   //child "aint" IContainer
}
BFree
fonte
@Mehrdad - Anulável? permitiria que ele funcionasse, não que isso deva ser usado. É apenas um exemplo de uma maneira mais feia.
Stevehipwell
@ Steveo3000: Sim, mas você deveria mencionar explicitamente? é a ascláusula obj as inté sempre um erro de tempo de compilação.
Mehrdad Afshari
@Mehrdad - Concordo, o BFree poderia editar seu post para refletir isso. Nos dando 'obj as int?'.
Stevehipwell
@ Stevo3000: No entanto, não acho que haja algo errado com isso. O IContainer parece mais uma interface do que um tipo de valor. Só queria salientar que requer cuidado no tipo de valor e nem sempre é uma tradução direta da isforma.
Mehrdad Afshari
você pode opcionalmente fazer if (obj == padrão (IContainer)), que iria cuidar de tipos de valor e tipos de referência
Joseph
3

O isoperador avalia como resultado booleano, para que você possa fazer qualquer coisa que, de outra forma, seria capaz de fazer em um bool. Para negá-lo, use o !operador Por que você gostaria de ter um operador diferente apenas para isso?

Brian Rasmussen
fonte
5
Não é um operador diferente. Fiquei me perguntando se havia uma palavra-chave que me deixaria largar o conjunto extra de parênteses. É uma grande escolha, mas fiquei curioso.
Hugoware
OK, eu entendo. Pelos seus exemplos, tive a impressão de que você estava procurando um novo operador dedicado.
Brian Rasmussen
Eu acho que ter um operador tão especial é ruim, porque teremos esse caminho (explicado de qualquer maneira) e, se tivermos outra operação, há duas maneiras de conseguir a mesma coisa: pode ser confuso.
BuddhiP
3

O método de extensão IsNot<T>é uma ótima maneira de estender a sintaxe. Tenha em mente

var container = child as IContainer;
if(container != null)
{
  // do something w/ contianer
}

executa melhor do que fazer algo como

if(child is IContainer)
{
  var container = child as IContainer;
  // do something w/ container
}

No seu caso, não importa como você está retornando do método. Em outras palavras, tenha cuidado para não fazer a verificação do tipo e a conversão do tipo imediatamente após.

Jeff
fonte
3

Embora isso não evite o problema de parênteses, para o bem das pessoas que chegam aqui pelo Google, deve-se mencionar que existe uma sintaxe mais nova (a partir do C # 7) para tornar o restante do seu código um pouco mais limpo:

if (!(DocumentPart is IContainer container)) { return; }
foreach(DocumentPart child in container.Children) {
    ...

Isso evita a conversão dupla, a verificação nula e a disponibilidade de uma variável nos escopos em que ela pode ser nula.

StriplingWarrior
fonte
2

Enquanto o operador IS é normalmente o melhor caminho, há uma alternativa que você pode usar em algumas circunstâncias. Você pode usar o operador as e testar como nulo.

MyClass mc = foo as MyClass;
if ( mc == null ) { }
else {}
Muad'Dib
fonte
2

C # 9 (para ser lançado com .NET 5) incluirá os padrões lógicos and, ore not, o que nos permite escrever este mais elegante:

if (child is not IContainer) { ... }

Da mesma forma, esse padrão pode ser usado para verificar se há nulo:

if (child is not null) { ... }

Você pode encontrar mais detalhes sobre o problema do Github que acompanha essa alteração.

Thorkil Holm-Jacobsen
fonte
-2
if (child is IContainer ? false : true)
Ternário
fonte