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.