强制执行所需的函数调用
我在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
是否有可能做到这一点?例如抛出异常
一般来说:是否可以编写必须在其上调用特定函数的Cclass?
在Status类的C ++版本中,我可以对析构函数中的一些私有布尔bIsChecked编写测试,并在有人不检查此实例时响起一些钟声。
C#中的等效选项是什么?
我在某处读到"我们不想在Cclass中使用析构函数"
IDisposable接口的Dispose方法是否是一个选项?
在这种情况下,没有没有可用的非托管资源。
此外,还不能确定GC何时处置对象。
当它最终被处置时,是否仍有可能知道我们在何时何地忽略了该特定Status实例?
" using"关键字确实有帮助,但是同样,懒惰的调用者不需要它。
解决方案
回答
我知道这不能直接回答问题,但是如果函数中出现"问题"(意外情况),我认为我们应该抛出异常,而不是使用状态返回码。
然后将其留给调用方以捕获并处理此异常(如果可以),或者允许它在调用方无法处理这种情况时进行处理。
如果合适,抛出的异常可以是自定义类型。
对于预期的替代结果,我同意@Jon Limjap的建议。我喜欢布尔返回类型,并在方法名称前加上" Try",例如:
bool TryMyFunction(out Status status) { }
回答
我们可以通过以下方式引发异常:
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语句检查状态比处理异常要高效得多。如果确实是例外情况,则一定要扔掉……但是要说,如果我们无法打开设备,则最好还是坚持使用返回码。
回答
让编译器检查而不是通过表达式检查肯定会很好。 :/
虽然看不到任何方法...
回答
将Status用作返回值使我想起了C编程的"过去",如果某些事情不起作用,则返回小于0的整数。
如果(如我们所说的)出现问题,如果抛出异常,会更好吗?如果某些"惰性代码"没有捕获异常,那么我们肯定会知道。
回答
当返回的HTTP状态代码是错误代码时,即使System.Net.WebRequest也会引发异常。处理它的典型方法是在其周围包装try / catch。我们仍然可以忽略catch块中的状态代码。
但是,我们可以具有Action <Status>的参数,以便强制调用方传递一个接受状态的回调函数,然后检查是否已调用该状态。
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 <Func <Status,bool >>,然后可以分析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());
回答
如果我们确实要要求用户检索MyFunction的结果,则可能要使其无效,并使用out或者ref变量,例如,
void MyFunction(out Status status) { }
它可能看起来很丑陋,但至少可以确保将变量传递到函数中,该变量将获取我们需要它获取的结果。
@伊恩
异常的问题在于,如果某件事发生得太频繁,则我们可能会花费过多的系统资源来处理异常。确实应该将异常用于异常错误,而不是完全预期的消息。
回答
GCC有一个warn_unused_result
属性,非常适合这类事情。也许Microsoft编译器具有类似的功能。
回答
@Paul,我们可以在编译时使用可扩展C#做到这一点。
回答
我认为我们不应该强迫别人检查状态,而应该假设程序员意识到不这样做的风险,并有理由采取这种行动。我们不知道将来如何使用该功能,并且这样的限制只会限制可能性。
回答
我相当确定我们无法从方法中获得所需的效果作为返回值。 Cjust不能做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方法将引发异常。我没有宣称使用这种技术的智慧-实际上在所有"出了点问题"的示例中,例外肯定是更好的选择-但我认为它与原始请求尽可能接近。