可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a method like...
int f() {
try {
int i = process();
return i;
} catch(Exception ex) {
ThrowSpecificFault(ex);
}
}
This produces a compiler error, "not all code paths return a value". But in my case ThrowSpecificFault() will always throw (the appropriate) exception. So I am forced to a put a return value at the end but this is ugly.
The purpose of this pattern in the first place is because "process()" is a call to an external web service but need to translate a variety of different exceptions to match a client's expected interface (~facade pattern I suppose).
Any cleaner way to do this?
回答1:
I suggest that you convert ThrowSpecificFault(ex)
to throw SpecificFault(ex)
; the SpecificFault
method would return the exception object to be thrown rather than throwing it itself. Much cleaner.
This is the pattern recommended by Microsoft's guidelines (find the text "Use exception builder methods").
回答2:
Right now a return type can be a type, or "void" meaning "no return type". We could in theory add a second special return type "never", which has the semantics you want. The end point of an expression statement consisting of a call to a "never" returning method would be considered unreachable, and so it would be legal in every context in C# in which a "goto", "throw" or "return" is legal.
It is highly unlikely that this will be added to the type system now, ten years in. Next time you design a type system from scratch, remember to include a "never" type.
回答3:
The problem here, is that if you go into the catch
block in f()
your function will never return a value. This will result in an error because you declared your function as int
which means you told the compiler that your method will return an integer.
The following code will do what you are looking for and always return an integer.
int f() {
int i = 0;
try {
i = process();
} catch(Exception ex) {
ThrowSpecificFault(ex);
}
return i;
}
put the return statement at the end of your function and you will be fine.
It's always a good idea to ensure your method will always return a value no matter what execution path your application goes through.
回答4:
You can do like this:
catch (Exception ex)
{
Exception e = CreateSpecificFault(ex);
throw e;
}
回答5:
No.
Imagine if ThrowSpecificFault
were defined in a separate DLL.
If you modify the DLL to not throw an exception, then run your program without recompiling it, what would happen?
回答6:
You have three options:
Always return i but pre-declare it:
int f() {
int i = 0; // or some other meaningful default
try {
i = process();
} catch(Exception ex) {
ThrowSpecificFault(ex);
}
return i;
}
Return the exception from the method and throw that:
int f() {
try {
int i = process();
return i;
} catch(Exception ex) {
throw GenerateSpecificFaultException(ex);
}
}
Or create a custom Exception class and throw that:
int f() {
try {
int i = process();
return i;
} catch(Exception ex) {
throw new SpecificFault(ex);
}
}
回答7:
How about:
int f() {
int i = -1;
try {
i = process();
} catch(Exception ex) {
ThrowSpecificFault(ex);
}
return i;
}
回答8:
Yes.
Don't expect ThrowSpecificFault() to throw the exception. Have it return the exception, then throw it here.
It actually makes more sense too. You don't use exceptions for the 'normal' flow, so if you throw an exception every time, the exception becomes the rule. Create the specific exception in the function, and throw it here because it is an exception to the flow here..
回答9:
I suppose you could make ThrowSpecificFault return an Object, and then you could
return ThrowSpecificFault(ex)
Otherwise, you could rewrite ThrowSpecificFault as a constructor for an Exception subtype, or you could just make ThrowSpecificFault into a factory that creates the exception but doesn't throw it.
回答10:
In you case but that is Your knowledge not the compiler. There is now way to say that this method for sure will throw some nasty exception.
Try this
int f() {
try {
return process();
} catch(Exception ex) {
ThrowSpecificFault(ex);
}
return -1;
}
You can also use the throw key word
int f() {
try {
return process();
} catch(Exception ex) {
throw ThrowSpecificFault(ex);
}
}
But then that method should return some exception instead of throwing it.
回答11:
Use Unity.Interception to clean up the code. With interception handling, your code could look like this:
int f()
{
// no need to try-catch any more, here or anywhere else...
int i = process();
return i;
}
All you need to do in the next step is to define an interception handler, which you can custom tailor for exception handling. Using this handler, you can handle all exceptions thrown in your app. The upside is that you no longer have to mark up all your code with try-catch blocks.
public class MyCallHandler : ICallHandler, IDisposable
{
public IMethodReturn Invoke(IMethodInvocation input,
GetNextHandlerDelegate getNext)
{
// call the method
var methodReturn = getNext().Invoke(input, getNext);
// check if an exception was raised.
if (methodReturn.Exception != null)
{
// take the original exception and raise a new (correct) one...
CreateSpecificFault(methodReturn.Exception);
// set the original exception to null to avoid throwing yet another
// exception
methodReturn.Exception = null;
}
// complete the invoke...
return methodReturn;
}
}
Registering a class to the handler can be done via a configuration file, or programmatically. The code is fairly straightforward. After registration, you instantiate your objects using Unity, like this:
var objectToUse = myUnityContainer.Resolve<MyObjectToUse>();
More on Unity.Interception:
http://msdn.microsoft.com/en-us/library/ff646991.aspx