执行所需的函数调用

时间:2008-08-21 09:57:26

标签: c# garbage-collection dispose destructor

我在C#中有一个“状态”类,使用如下:

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

你明白了。 MyFunction 的所有呼叫者都应该检查返回的状态:

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

懒惰的来电者可以忽略状态。

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

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

有可能让这个变得不可能吗?例如抛出异常

一般:是否可以编写 来调用某个函数的C#类?

在Status类的C ++版本中,我可以在析构函数中的某些私有bool bIsChecked上编写测试,并在有人不检查此实例时响铃。

C#中的等效选项是什么? 我在某处读到“你不想在你的C#类中使用析构函数”

IDisposable接口的Dispose方法是一个选项吗?

在这种情况下,没有免费的非托管资源。 此外,当 GC将处置对象时,未确定。 当它最终被处置时,是否仍然可以知道忽略该特定Status实例的位置和时间? “使用”关键字确实有帮助,但对于懒惰的来电者来说,它不是必需的

11 个答案:

答案 0 :(得分:10)

我知道这不能直接回答你的问题,但是如果你的功能中出现“出问题”(意外情况),我认为你应该抛出异常,而不是使用状态返回码。

然后将其留给调用者来捕获并处理此异常(如果可以),或者如果调用者无法处理该情况则允许它传播。

如果这是合适的,抛出的异常可以是自定义类型。

对于预期的替代结果,我同意@Jon Limjap的建议。我喜欢bool返回类型,并在方法名称前加上“Try”,la:

bool TryMyFunction(out Status status)
{
}

答案 1 :(得分:7)

如果你真的想要求用户检索MyFunction的结果,你可能想要取消它而使用out或ref变量,例如,

void MyFunction(out Status status)
{
}

它可能看起来很难看,但至少它确保将一个变量传递给该函数,该函数将获取您需要它的结果。

@Ian,

异常的问题在于,如果某些事情经常发生,您可能会为异常花费太多系统资源。实际应该使用例外错误,而不是完全预期的消息。

答案 2 :(得分:3)

当返回的HTTP状态代码是错误代码时,即使System.Net.WebRequest也会引发异常。处理它的典型方法是在它周围包装一个try / catch。您仍然可以忽略catch块中的状态代码。

但是,您可以使用Action<的参数。状态>这样调用者就被迫传递一个接受状态的回调函数,然后检查他们是否调用了它。

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());

如果你担心他们在没有做任何事情的情况下调用IsOK(),请使用Expression&lt; FUNC&LT;状态,布尔&GT;&GT;相反,然后您可以分析lambda以查看它们对状态的作用:

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());

或完全放弃状态类,让它们在一个代表中传递成功,另一个代表传递错误:

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

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

答案 3 :(得分:2)

我相当肯定你不能从方法中获得你想要的效果作为返回值。 C#只是不能做C ++可以做的一些事情。然而,获得类似效果的一种有点丑陋的方法如下:

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

在一个非常简单的例子中,您通过调用Parent.RequestToy,并向其传递委托来获取Toy的实例。该方法不是返回玩具,而是立即使用玩具调用委托,玩具必须在返回之前调用PutAway,否则RequestToy方法将抛出异常。我没有声称使用这种技术的智慧 - 实际上在所有“出问题”的例子中,例外几乎肯定是一个更好的选择 - 但我认为它会尽可能接近您的原始请求。

答案 4 :(得分:1)

使用Status作为返回值会记住C编程的“旧时代”,当你返回一个低于0的整数时,如果某些东西不起作用。

如果你把(当你说的话)出错了时抛出异常会不会更好?如果某些“懒惰代码”没有捕获到您的异常,您肯定会知道。

答案 5 :(得分:1)

我认为你不应该强迫某人检查状态,而应该假设程序员意识到这样做的风险,并且有理由采取这种行动。您不知道将来如何使用该功能,并且限制此类限制只会限制其可能性。

答案 6 :(得分:0)

您可以通过以下方式抛出异常:

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

上述例外可完全根据您的要求进行定制。

我要说的一件事是,我会将它留给调用者来检查返回代码,它只是提供手段和界面的责任。此外,使用返回代码并使用if语句检查状态而不是检查异常会更有效。如果它确实是 Exceptional 情况,那么一定要扔掉......但是如果你没有打开设备,那么坚持使用返回代码可能会更为谨慎。

答案 7 :(得分:0)

让编译器检查而不是通过表达式确实很好。 :/ 虽然没看到任何办法......

答案 8 :(得分:0)

GCC有一个warn_unused_result属性,非常适合这类事情。也许Microsoft编译器有类似的东西。

答案 9 :(得分:0)

@Paul你可以在编译时使用Extensible C#来完成。

答案 10 :(得分:0)

如果代码发出请求的对象仅由单个线程(*)使用,则有时可能有用的一种模式是让对象保持错误状态,并说如果操作失败,则对象将是在错误状态被重置之前不可用(未来的请求应该立即失败,最好是通过抛出包括先前失败和新请求的信息的立即异常)。在调用代码恰好预期出现问题的情况下,这可能允许调用代码比抛出异常时更干净地处理问题;调用代码未忽略的问题通常会在它们发生后很快触发异常。

(*)如果多个线程将访问资源,请为每个线程创建一个包装器对象,并让每个线程的请求都通过自己的包装器。

这种模式即使在例外情况不适合的情况下也可以使用,在这种情况下有时可能非常实用。然而,一般而言,try / do模式的一些变化通常更好。除非调用者明确指出(通过使用TryXX方法)预期失败,否则方法会在失败时抛出异常。如果打电话者说失败是预期但不处理它们,那就是他们的问题。可以使用上面的方案将try / do与第二层保护结合起来,但我不确定它是否值得花费。