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...

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...

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");
}

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?


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
}

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.


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
}

@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?


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.


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.


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 {}

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.


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.