Pregunta

Tengo un "Estado" de la clase en C#, utilizado como esta:

Status MyFunction()
{
   if(...) // something bad
     return new Status(false, "Something went wrong")
   else
     return new Status(true, "OK");
}

Usted consigue la idea.Todos los contactos de MyFunction debe comprobar el Estado devuelto:

Status myStatus = MyFunction();
if ( ! myStatus.IsOK() )
   // handle it, show a message,...

Perezoso personas que llaman, sin embargo, puede ignorar el Estado.

MyFunction(); // call function and ignore returned Status

o

{
  Status myStatus = MyFunction(); 
} // lose all references to myStatus, without calling IsOK() on it

Es posible hacer esto imposible?por ejemplo,un tiro de excepción

En general:es posible escribir una clase de C# en el que han para llamar a una función determinada?

En la versión de C++ de la Situación de la clase, me puede escribir un ensayo sobre algunos private bool bIsChecked en el destructor y el anillo de unas campanas, cuando alguien no marque esta instancia.

¿Cuál es la opción equivalente en C#?Leí en alguna parte que "no queremos Que un destructor en su clase de C#"

Es el método Dispose () de la interfaz IDisposable una opción?

En este caso no hay recursos no administrados libre.Además, no está determinado cuando la GC se encargará de eliminar el objeto.Cuando el tiempo se eliminan, ¿es posible saber donde y cuando se ignora que el Estado específico de la instancia?El "uso" de la palabra clave ayuda, pero de nuevo, no es se requiere para los perezosos que llaman.

¿Fue útil?

Solución

Estoy bastante seguro de que usted no puede conseguir el efecto que desea como valor de retorno de un método.C# no puede hacer algunas de las cosas de C++.Sin embargo, un poco fea manera de conseguir un efecto similar es el siguiente:

using System;

public class Example
{
    public class Toy
    {
        private bool inCupboard = false;
        public void Play() { Console.WriteLine("Playing."); }
        public void PutAway() { inCupboard = true; }
        public bool IsInCupboard { get { return inCupboard; } }
    }

    public delegate void ToyUseCallback(Toy toy);

    public class Parent
    {
        public static void RequestToy(ToyUseCallback callback)
        {
            Toy toy = new Toy();
            callback(toy);
            if (!toy.IsInCupboard)
            {
                throw new Exception("You didn't put your toy in the cupboard!");
            }
        }
    }

    public class Child
    {
        public static void Play()
        {
            Parent.RequestToy(delegate(Toy toy)
            {
                toy.Play();
                // Oops! Forgot to put the toy away!
            });
        }
    }

    public static void Main()
    {
        Child.Play();
        Console.ReadLine();
    }
}

En el ejemplo muy simple, se obtiene una instancia de Juguete llamando a los Padres.RequestToy, y pasando a un delegado.En lugar de devolver el juguete, el método se llama inmediatamente al delegado con el juguete, que debe llamar de Separación antes de que se devuelve, o la RequestToy método arrojará una excepción.Yo no se afirma la sabiduría de la utilización de esta técnica -- de hecho, en toda "algo salió mal" ejemplos una excepción es casi seguramente una mejor apuesta, pero yo creo que se trata tan cerca como usted puede conseguir a su solicitud original.

Otros consejos

Sé que esto no responde a su pregunta directamente, pero si "algo salió mal" dentro de su función (circunstancias inesperadas) creo que se debe lanzar una excepción en lugar de utilizar el estado de los códigos de retorno.

A continuación, dejar a la persona que llama para detectar y controlar esta excepción si se puede, o permitir a propagarse si la persona es incapaz de manejar la situación.

La excepción podría ser de un tipo personalizado si esto es apropiado.

Para espera alternativa de resultados, estoy de acuerdo con @Jon Limjap la sugerencia.Soy aficionado de un tipo bool tipo de retorno y como prefijo el nombre del método con "Try", a la:

bool TryMyFunction(out Status status)
{
}

Si usted realmente desea requerir que el usuario recupere el resultado de MyFunction, usted podría desea anular lugar y el uso de una o ref variable, por ejemplo,

void MyFunction(out Status status)
{
}

Puede parecer feo, pero al menos se asegura de que una variable se pasa a la función que va a recoger el resultado que usted necesita para recoger.

@Ian,

El problema con las excepciones es que si es algo que ocurre demasiado a menudo, usted puede estar gastando demasiado de los recursos del sistema para la excepción.Una excepción que realmente debe ser utilizado para excepcional errores, no totalmente de espera de mensajes.

Incluso El Sistema.Net.WebRequest lanza una excepción cuando se devuelve el código de estado HTTP que es un código de error.La típica forma de manejarlo es envolver un try/catch a su alrededor.Usted todavía puede ignorar el código de estado en el bloque catch.

Sin embargo, puede tener un parámetro de Acción< Estado> de modo que la persona que llama se ve obligado a pasar una función de devolución de llamada que acepta un estado y, a continuación, comprobar para ver si ellos lo llamaban.

void MyFunction(Action<Status> callback)
 { bool errorHappened = false;

   if (somethingBadHappend) errorHappened = true;

   Status status = (errorHappend)
                     ? new Status(false, "Something went wrong")
                     : new Status(true, "OK");
   callback(status)

   if (!status.isOkWasCalled) 
     throw new Exception("Please call IsOK() on Status"). 
 }

MyFunction(status => if (!status.IsOK()) onerror());

Si usted está preocupado acerca de las llamadas de IsOK() sin hacer nada, el uso de la Expresión< Func< Estado,bool>> en lugar de y, a continuación, puede analizar la lambda para ver lo que hacen con el estado:

void MyFunction(Expression<Func<Status,bool>> callback)
 { if (!visitCallbackExpressionTreeAndCheckForIsOKHandlingPattern(callback))
     throw new Exception
                ("Please handle any error statuses in your callback");


   bool errorHappened = false;

   if (somethingBadHappend) errorHappened = true;

   Status status = (errorHappend)
                     ? new Status(false, "Something went wrong")
                     : new Status(true, "OK");

   callback.Compile()(status);
 }

MyFunction(status => status.IsOK() ? true : onerror());

O renunciar a la condición de clase en conjunto y hacer pasar de un delegado para el éxito y la otra para un error:

void MyFunction(Action success, Action error)
 { if (somethingBadHappened) error(); else success();
 }

MyFunction(()=>;,()=>handleError()); 

Mediante el Estado como un retorno de valor me recuerda de los "viejos tiempos" de programación en C, cuando regresó de un entero por debajo de 0 si algo no funciona.

¿No sería mejor si usted lanza una excepción cuando (como usted dice) algo salió mal?Si algunos "perezoso código de" no te llama la excepción, usted estará seguro.

En lugar de forzar a alguien para comprobar el estado, creo que se debería asumir que el programador es consciente de ello los riesgos de no hacerlo así, y tiene una razón para tomar ese curso de acción.Usted no sabe cómo la función va a ser utilizado en el futuro y la colocación de una limitación, como que sólo limita las posibilidades.

Que seguro será bueno tener el compilador comprueba que, en lugar de a través de una expresión.:/ No veo ninguna manera de hacer que a pesar de...

Usted puede lanzar una excepción por:

throw MyException;


[global::System.Serializable]
        public class MyException : Exception
        {
        //
        // For guidelines regarding the creation of new exception types, see
        //    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconerrorraisinghandlingguidelines.asp
        // and
        //    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp07192001.asp
        //

        public MyException () { }
        public MyException ( string message ) : base( message ) { }
        public MyException ( string message, Exception inner ) : base( message, inner ) { }
        protected MyException (
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context )
            : base( info, context ) { }
    }

La anterior excepción es completamente personalizable para adaptarse a sus requisitos.

Una cosa que quiero decir es esto, que yo dejaría a la persona que llama para comprobar el código de retorno, es su responsabilidad que acaba de proporcionar los medios y la interfaz.También, es mucho más eficiente el uso de los códigos de retorno y comprobar el estado con una declaración de si en lugar de trhowing excepciones.Si es que realmente es un Excepcional circunstancia, entonces por todos los medios a poca distancia de...pero dicen que si no se pudo abrir el dispositivo, entonces podría ser más prudente a seguir con el código de retorno.

@Pablo, que podría hacerlo en tiempo de compilación Extensible C#.

GCC tiene una warn_unused_result atributo que es ideal para este tipo de cosas.Tal vez los compiladores de Microsoft tienen algo similar.

Un patrón que puede ser útil a veces, si el objeto a que problemas de código de solicitudes sólo serán utilizados por un único subproceso(*) es que el objeto de mantener un estado de error, y decir que si falla una operación del objeto será inutilizable hasta que el error de estado de reset (en el futuro las solicitudes deben fallar de inmediato, preferiblemente por lanzar inmediatamente una excepción, que incluye información sobre el fracaso anterior y la nueva solicitud).En los casos donde el código de llamada pasa a anticipar un problema, esto puede permitir que el código de llamada para manejar el problema de una manera más limpia que si una excepción fueron arrojados;problemas que no son ignorados por el código de llamada generalmente terminan desencadenando una excepción muy pronto después de que ocurren.

(*) Si el recurso se tendrá acceso por varios subprocesos, crear un objeto contenedor para cada hilo, y cada hilo de peticiones de ir a través de su propia envoltura.

Este patrón es útil incluso en contextos en los que las excepciones no son, y a veces puede ser muy práctico en estos casos.En general, sin embargo, algunos de variación de la prueba/do patrón es generalmente mejor.Han métodos de lanzar la excepción en caso de error, a menos que la persona que llama se indica de manera explícita (mediante el uso de un TryXX método) que los errores que se espera.Si la persona que llama dice son los fallos que se esperaba, pero no los toques, que es su problema.Podría combinar el try/hacer con una segunda capa de protección utilizando el esquema de arriba, pero no estoy seguro de si valdría la pena el costo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top