C# 强制执行所需的函数调用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19553/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-01 09:03:09  来源:igfitidea点击:

Enforcing required function call

提问by jan

I have a "Status" class in C#, used like this:

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

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

You get the idea. All callers of MyFunction shouldcheck the returned Status:

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

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

Lazy callers however can ignore the Status.

然而,懒惰的调用者可以忽略状态。

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

or

或者

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

Is it possible to make this impossible? e.g. an throw exception

有可能使这成为不可能吗?例如抛出异常

In general: is it possible to write a C# class on which you haveto call a certain function?

一般而言:是否可以编写一个必须在其上调用某个函数的 C# 类?

In the C++ version of the Status class, I can write a test on some private bool bIsChecked in the destructorand ring some bells when someone doesn't check this instance.

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

What is the equivalent option in C#? I read somewhere that "You don't want a destructor in your C# class"

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

Is the Dispose method of the IDisposable interface an option?

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

In this case there are no unmanaged resources to free. Additionally, it is not determined whenthe GC will dispose the object. When it eventually gets disposed, is it still possible to know where and when you ignored that specific Status instance? The "using" keyword does help, but again, it is not requiredfor lazy callers.

在这种情况下,没有要释放的非托管资源。此外,它没有确定,GC将配置的对象。当它最终被处置时,是否仍然有可能知道您在何时何地忽略了那个特定的 Status 实例?“using”关键字确实有帮助,但同样,懒惰的调用者不需要它。

采纳答案by Weeble

I am fairly certain you can't get the effect you want as a return value from a method. C# just can't do some of the things C++ can. However, a somewhat ugly way to get a similar effect is the following:

我相当肯定你不能从方法中获得你想要的效果作为返回值。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();
    }
}

In the very simple example, you get an instance of Toy by calling Parent.RequestToy, and passing it a delegate. Instead of returning the toy, the method immediately calls the delegate with the toy, which must call PutAway before it returns, or the RequestToy method will throw an exception. I make no claims as to the wisdom of using this technique -- indeed in all "something went wrong" examples an exception is almost certainly a better bet -- but I think it comes about as close as you can get to your original request.

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

回答by Ian Nelson

I know this doesn't answer your question directly, but if "something went wrong" within your function (unexpected circumstances) I think you should be throwing an exception rather than using status return codes.

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

Then leave it up to the caller to catch and handle this exception if it can, or allow it to propogate if the caller is unable to handle the situation.

然后让调用者在可以的情况下捕获并处理此异常,或者如果调用者无法处理这种情况,则允许它传播。

The exception thrown could be of a custom type if this is appropriate.

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

For expectedalternative results, I agree with @Jon Limjap's suggestion. I'm fond of a bool return type and prefixing the method name with "Try", a la:

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

bool TryMyFunction(out Status status)
{
}

回答by TK.

You can throw an exception by:

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

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

The above exception is fully customizable to your requirements.

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

One thing I would say is this, I would leave it to the caller to check the return code, it is their responsability you just provide the means and interface. Also, It is a lot more efficient to use return codes and check the status with an if statement rather than trhowing exceptions. If it really is an Exceptionalcircumstance, then by all means throw away... but say if you failed to open a device, then it might be more prudent to stick with the return code.

我要说的一件事是,我会将它留给调用者检查返回码,您只提供方法和接口是他们的责任。此外,使用返回码并使用 if 语句检查状态比抛出异常更有效。如果确实是特殊情况,那么无论如何都要扔掉……但是如果您无法打开设备,那么坚持使用返回码可能会更加谨慎。

回答by fulmicoton

That would sure be nice to have the compiler check that rather than through an expression. :/ Don't see any way to do that though...

让编译器检查而不是通过表达式肯定会很好。:/虽然没有看到任何方法来做到这一点......

回答by Auron

Using Status as a return value remembers me of the "old days" of C programming, when you returned an integer below 0 if something didn't work.

使用 Status 作为返回值让我想起了 C 编程的“旧时代”,当某些东西不起作用时,你返回一个小于 0 的整数。

Wouldn't it be better if you throw an exception when (as you put it) something went wrong? If some "lazy code" doesn't catch your exception, you'll know for sure.

如果在(如您所说)出现问题时抛出异常不是更好吗?如果某些“惰性代码”没有捕捉到您的异常,您肯定会知道。

回答by Mark Cidade

Even System.Net.WebRequest throws an exception when the returned HTTP status code is an error code. The typical way to handle it is to wrap a try/catch around it. You can still ignore the status code in the catch block.

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

You could, however, have a parameter of Action< Status> so that the caller is forced to pass a callback function that accepts a status and then checking to see if they called it.

但是,您可以有一个 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());

If you're worried about them calling IsOK() without doing anything, use Expression< Func< Status,bool>> instead and then you can analyse the lambda to see what they do with the status:

如果您担心他们在不执行任何操作的情况下调用 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());

Or forego the status class altogether and make them pass in one delegate for success and another one for an error:

或者完全放弃状态类,让他们通过一个代表成功,另一个代表错误:

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

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

回答by Jon Limjap

If you really want to require the user to retrieve the result of MyFunction, you might want to void it instead and use an out or ref variable, e.g.,

如果您真的想要求用户检索 MyFunction 的结果,您可能希望将其设为 void 并使用 out 或 ref 变量,例如,

void MyFunction(out Status status)
{
}

It might look ugly but at least it ensures that a variable is passed into the function that will pick up the result you need it to pick up.

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

@Ian,

@伊恩,

The problem with exceptions is that if it's something that happens a little too often, you might be spending too much system resources for the exception. An exception really should be used for exceptionalerrors, not totally expected messages.

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

回答by pauldoo

GCC has a warn_unused_resultattribute which is ideal for this sort of thing. Perhaps the Microsoft compilers have something similar.

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

回答by Mark Cidade

@Paul you could do it at compile time with Extensible C#.

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

回答by kemiller2002

Instead of forcing someone to check the status, I think you should assume the programmer is aware of this risks of not doing so and has a reason for taking that course of action. You don't know how the function is going to be used in the future and placing a limitation like that only restricts the possibilities.

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