C #: palabra clave 'is' y buscando Not

287

Esta es una pregunta tonta, pero puede usar este código para verificar si algo es un tipo particular ...

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

¿Hay alguna forma más elegante de verificar la instancia "NO"?

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

Sí, sí ... pregunta tonta ...

Debido a que hay alguna pregunta sobre cómo se ve el código, es solo un simple retorno al comienzo de un método.

public void Update(DocumentPart part) {
    part.Update();
    if (!(DocumentPart is IContainer)) { return; }
    foreach(DocumentPart child in ((IContainer)part).Children) {
       //...etc...
Hugoware
fuente
105
Personalmente, me gusta el "niño no se está volviendo loco ...". Estoy votando para que esa palabra clave se coloque en C # 5
Joseph
¿Me interesa saber la situación en la que usarías esto? ¿Cómo se ve la parte "else" de este código y no puedes simplemente invertir la prueba? Si su código dice "si el niño no es un IContainer, entonces arroje excepciones" o "si el niño no es un IContainer, entonces tal vez sea un IFoo, así que lo intentaré a continuación", ¿no hay allí una declaración implícita de otra cosa? Probablemente me estoy perdiendo algo.
Martin Peck
1
@ MartinPeck, puede que no haya una cláusula else. Esa es la razón por la que busqué esto.
Joshua Walsh
@MartinPeck aquí hay una muestra: if (!(argument is MapsControlViewModel vm)) { return; }- Podría invertir el if y poner el resto del método whoooole dentro de los corchetes if, pero luego obtendría el código del árbol de Navidad, con muchos corchetes de cierre al final del método. Eso es mucho menos legible.
ANeves
quizás lo que necesitamos en general sean ifnotdeclaraciones
Dave Cousineau

Respuestas:

301
if(!(child is IContainer))

es el único operador que va (no hay IsNotoperador).

Puede crear un método de extensión que lo haga:

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

y luego úsalo para:

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

Y puedes seguir tu tema:

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

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

Actualización (considerando el fragmento de código del OP):

Como en realidad está emitiendo el valor después, podría usar asen su lugar:

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
fuente
1
ck: Quise decir en el sentido de operadores, no hay IsNotnada.
Mehrdad Afshari
55
Si. Estoy bromeando en caso de que no sea obvio.
Mehrdad Afshari
111

Puedes hacerlo de esta manera:

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

if (a is TextReader == false)
{
   Console.WriteLine("failed");
}
cjk
fuente
2
@Frank - sí, la palabra clave is da un valor booleano, que puedes comparar con false
cjk
32
@Frank funciona porque istiene mayor prioridad en relación con ==. La única razón por la que no puede usar !x is fes que tiene menos prioridad que !.
Mehrdad Afshari
Me gusta esto, pero no parece funcionar bien al introducir una variable, aunque debería. if (a is TextReader reader == false)"debería" funcionar, pero no le permitirá usar la variable en la ruta verdadera diciendo que podría no haberse inicializado.
Dave Cousineau el
@DaveCousineau: normalmente, debe marcar e introducir una variable cuando desee utilizar la variable introducida. No estoy seguro de cómo sería útil la variable si fallara la verificación de tipo (descargo de responsabilidad: encuentro que la función de "Coincidencia de patrones" está mal nombrada y tiene un olor de código tan malo como el uso de outparámetros)
StingyJack
@StingyJack hay algún tipo de falla en la ruta verdadera , la variable se considera no inicializada. incluso si dices if (a is TextReader reader == true)que piensa que la variable no está inicializada.
Dave Cousineau
11

¿Por qué no solo usar el otro?

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

¿Está bien, es familiar y simple?

Mark Broadhurst
fuente
3
No tiene nada de malo, esta es solo una cuestión básica. Quería salir inmediatamente de una función si algo no era un tipo particular. Lo he hecho (! (El niño es algo)) para siempre, pero pensé en asegurarme de que no hubiera una mejor manera.
Hugoware
1
Con el código de muestra en la pregunta, esto significaría un corchete if vacío. Eso no suena como una alternativa sensata.
ANeves
9

La forma en que la tiene está bien, pero podría crear un conjunto de métodos de extensión para hacer "una forma más elegante de verificar la instancia 'NO'".

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

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

Entonces podrías escribir:

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

Esto no ha sido mencionado todavía. Funciona y creo que se ve mejor que usar!(child is IContainer)

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

issintaxis:, expr is constant donde expr es la expresión para evaluar, y constante es el valor para probar.

Todd Skelton
fuente
3
De manera similar podrías hacer if (part as IContainer is null). Sinceramente, no estoy seguro de cuál es mejor.
Flynn1179
5

¿Feo? Estoy en desacuerdo. La única otra forma (personalmente creo que esto es "más feo"):

var obj = child as IContainer;
if(obj == null)
{
   //child "aint" IContainer
}
BFree
fuente
@Mehrdad - Anulable? permitiría que funcione, no es que esto deba usarse. Es solo un ejemplo de una forma más fea.
stevehipwell 01 de
@ Steveo3000: Sí, pero ¿debería mencionarlo explícitamente? Es la ascláusula. obj as intes siempre un error de tiempo de compilación.
Mehrdad Afshari
@Mehrdad - De acuerdo, BFree podría editar su publicación para reflejar esto. Dándonos 'obj como int?'.
stevehipwell 01 de
@ Stevo3000: Sin embargo, no creo que haya nada malo en ello. IContainer se siente como una interfaz en lugar de un tipo de valor. Solo quería señalar que requiere cuidado en el tipo de valor y no siempre es una traducción directa de la isforma.
Mehrdad Afshari
Opcionalmente, podría hacerlo if (obj == default (IContainer)), que se ocuparía de los tipos de valor y los tipos de referencia
Joseph
3

El isoperador evalúa a un resultado booleano, por lo que puede hacer cualquier cosa que de otro modo podría hacer en un bool. Para negarlo, use el !operador. ¿Por qué querrías tener un operador diferente solo para esto?

Brian Rasmussen
fuente
55
No es un operador diferente. Me preguntaba si había una palabra clave que me permitiera eliminar el conjunto adicional de parens. Es una gran elección, pero tenía curiosidad.
Hugoware
Está bien lo entiendo. A partir de sus ejemplos, tuve la impresión de que estaba buscando un nuevo operador dedicado.
Brian Rasmussen
Creo que tener un operador tan especial es malo, porque lo haremos de esta manera (explicado esto de todos modos), y si tuviéramos otra operación, entonces hay dos formas de lograr lo mismo, puede ser confuso.
BuddhiP
3

El método de extensión IsNot<T>es una buena manera de extender la sintaxis. Tenga en cuenta

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

funciona mejor que hacer algo como

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

En su caso, no importa ya que regresa del método. En otras palabras, tenga cuidado de no hacer tanto la verificación de tipo como la conversión de tipo inmediatamente después.

Jeff
fuente
3

Si bien esto no evita el problema de los paréntesis, por el bien de las personas que llegan aquí a través de Google, debe mencionarse que existe una sintaxis más nueva (a partir de C # 7) para que el resto de su código sea un poco más limpio:

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

Esto evita la doble conversión, la verificación nula y tener una variable disponible en ámbitos donde podría ser nula.

StriplingWarrior
fuente
2

Si bien el operador IS normalmente es la mejor manera, existe una alternativa que puede usar en algunas circunstancias. Puede usar el operador as y probar nulo.

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

C # 9 (que se publicará con .NET 5) incluirá los patrones lógicos and, ory not, lo que nos permite escribir esto más elegante:

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

Del mismo modo, este patrón se puede utilizar para verificar nulo:

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

Puede encontrar más detalles sobre el problema de Github que rastrea este cambio.

Thorkil Holm-Jacobsen
fuente
-2
if (child is IContainer ? false : true)
Ternario
fuente