I've inherited a Winforms program that uses some 3rd party software, for which I cannot see the source code. When calling the methods in this code, it is expected to connect to a reader, which the software checks for. Unfortunately, the connection seems to drop from time to time, and the hidden functions cause the program to fail silently. Is there a way to handle every call to this class' methods without using a try catch on every one?
The software looks something like this, with the Reader class being used such that we can access the same reader across multiple modules:
public class Reader
{
private ThirdPartyReader thirdPartyReader;
public ObjectReport QueryObjects()
{
return thirdPartyReader.QueryObjects();
}
public Settings QuerySettings()
{
return thirdPartyReader.QuerySettings();
}
}
public static class Extensions
{
public static TResult Try<TObject, TResult>(this TObject source, Func<TObject, TResult> method, string message = null)
{
try
{
return method(source);
}
catch (Exception e)
{
//Some Logging or whatever, optionally using the message parameter;
return default(TResult);
}
}
}
Usage:
var reader = new ThirdPartyReader();
var objects = reader.Try(x => x.QueryObjects());
var settings = reader.Try(x => x.QuerySettings(), "Error Reading Settings");
It's really not that different because you still have to do reader.Try(), instead of just calling methods, but it's a much abbreviated syntax.
Related
Note: This could very well be very C# specific language question, unrelated to WCF or web services at all.
There is a 3-party ASMX web service, which shall be used for data retrieval. I have created a generalized method called ExecuteCommand() which is used for every request against the web service. The purpose of this method is to handle cookie session/exceptions and other common logic. For each request a new channel shall be used, in order to simplify the disposal of unused resources.
The problem is that to use the ExecuteCommand() method - I have to initialize a channel each time, in order to be able to pass the method to be executed as an argument. Sorry if it sounds too complicated. Here is a usage example:
string color = "blue";
var channel = _strategyFactory.CreateChannel<CarServiceSoapChannel>();
var cars = WcfHelper.ExecuteCommand(channel, () => channel.GetCars(color));
// channel is null here. Channel was closed/aborted, depending on Exception type.
After ExecuteCommand() is called - channel is already disposed of. The reason why channel object is needed at all, is to be able to provide a method to be executed as a parameter! i.e.() => channel.GetCars(). To further support these words, here is the WcfHelper class internals:
public static class WcfHelper
{
public static Cookie Cookie { get; set; }
public static T ExecuteCommand<T>(IClientChannel channel, Expression<Func<T>> method)
{
T result = default(T);
try
{
// init operation context
using (new OperationContextScope(channel))
{
// set the session cookie to header
if (Cookie != null) {
HttpRequestMessageProperty request = new HttpRequestMessageProperty();
request.Headers["Cookie"] = cookie.ToString();
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = request;
}
// execute method
var compiledMethod = method.Compile();
result = compiledMethod.Invoke();
}
}
// do different logic for FaultException, CommunicationException, TimeoutException
catch (Exception)
{
throw;
}
finally
{
CloseOrAbortServiceChannel(channel);
channel = null;
}
return result;
}
private static void CloseOrAbortServiceChannel(ICommunicationObject communicationObject)
{
bool isClosed = false;
if (communicationObject == null || communicationObject.State == CommunicationState.Closed)
return;
try
{
if (communicationObject.State != CommunicationState.Faulted)
{
communicationObject.Close();
isClosed = true;
}
}
catch (Exception)
{
throw;
}
finally
{
if (!isClosed)
AbortServiceChannel(communicationObject);
}
}
private static void AbortServiceChannel(ICommunicationObject communicationObject)
{
try
{
communicationObject.Abort();
}
catch (Exception)
{
throw;
}
}
}
So the short question - it it possible to initialize a channel variable inside the ExecuteCommand method itself, while having a possibility to define, which method shall be executed inside ExecuteCommand for a given channel?
I am trying to accomplish something like this:
string color = "blue";
var cars = WcfHelper.ExecuteCommand<Car[], CarServiceSoapChannel>(channel => channel.GetCars(color));
or even
string color = "blue";
var cars = WcfHelper.ExecuteCommand<CarServiceSoapChannel>(channel => channel.GetCars(color));
Any other code improvement suggestions are welcomed but not mandatory, of course.
P.S. ASMX is added as a Service reference in Visual Studio. Therefore, there were some entities that automatically generated for the "CarService", such as - CarServiceSoapChannel interface, CarServiceSoapClient class and of course CarService interface containing methods of a web service. In the example above a ChannelFactory is used to create a channel for the CarServiceSoapChannel interface, hence, here is where the question name is coming from: Passing an interface method as a parameter. This could be a bit misleading, but I hope it's clear what I trying to accomplish from the description itself.
Update 25.05.2018
I followed the advice of #nvoigt and was able to achieve the result I wanted:
public static TResult ExecuteCommand<TInterface, TResult>(Func<TInterface, TResult> method)
where TInterface : IClientChannel
{
TResult result = default(TResult);
IClientChannel channel = null;
try
{
channel = StrategyFactory.CreateChannel<TInterface>();
// init operation context
using (new OperationContextScope(channel))
{
// set the session cookie to header
if (Cookie != null)
Cookie.SetCookieForSession();
// execute method
result = method((TInterface)channel);
}
}
catch (Exception)
{
throw;
}
finally
{
CloseOrAbortServiceChannel(channel);
channel = null;
}
return result;
}
This already achieves the initial goal. There is, however, one issue with this approach. In order to call the method - you have to explicitly specify the method's return parameter.
So to say, if you want to call the method - writing this is not enough:
var result = WcfHelper.ExecuteCommand<CarServiceSoapChannel>(channel => channel.IsBlue())
You will have to specifically specify, that the return type shall be boolean
var result = WcfHelper.ExecuteCommand<CarServiceSoapChannel, bool>(channel => channel.IsBlue())
I personally don't mind writing an extra bit of code, as it is still a big advantage over my initial method implementation, however, I am wondering in the new version could be improved?
For example, When I had just 1 generic TResult type in the method - the return type <TResult> could be omitted. This is no longer the case with 2 generics. In any case, thank you #nvoigt!
Your method signature should be:
public static TResult ExecuteCommand<TInterface>(Func<TInterface, TResult> method)
Then in your WcfHelper (which should probably not be static anymore because it needs a member _strategyFactory) you create a channel as before:
{
var channel = _strategyFactory.CreateChannel<CarServiceSoapChannel>();
return method(channel);
}
Obviously, you need to add all the fancy try/finally stuff again.
As you should have instances anyway now with the factory in the class as a member, you could put the service contract generic into your class to make it easier for users:
public class ConnectionToService<TInterface> : where TInterface : class
{
public TResult ExecuteCommand<TResult>(Func<TInterface, TResult> method)
{
var channel = _strategyFactory.CreateChannel<CarServiceSoapChannel>();
return method(channel);
}
}
Usage:
var service = new ConnectionToService<ICarService>();
var color = service.ExecuteCommand(s => s.GetColor());
Let's say you have two different libraries that you write for different format of files. One parses type A of file and one parses type B. Now I see that in most cases most folks say that the exceptions already defined (let's say we use C# and .NET) are enough to handle most situations. In this case I think that is OK that in both cases we if we encounter some problems when parsing we throw a InvalidDataException that tells us that the format is not correct.What happens when we must catch both exceptions in a higher level function that must do something with them?
void SomeHigherFunction()
{
try
{
int x = GetSomeDataFromFileA(); // throws InvalidDataException
int y = GetSomeDataFromFileB(); // throws InvalidDataException
}
catch(InvalidDataException) // what failed ?
{
ShowMessageWithWhatFileFailed() // what display ?
}
}
How do we know where the code failed? Should in this case be two different Exception types defined, one TypeAException and one TypeBException or is something easy here that I miss? I don't want to wrap each function and return a bool with the result. In this case I would wrap function A in a function and let it return true if successful and execute B further. But that is clearly a bad design and wrong use of exceptions. Should I just create new types in this cases ? Or how?
You can use Exception.Data Collection to pass the owner of the Exception and do something with it further.
I refactored your example a little, but i think this fits your requirements:
class Program
{
[Serializable]
public abstract class Parser
{
public int GetData()
{
bool error = true;
if (error)
{
InvalidDataException exception = new InvalidDataException();
exception.Data.Add("owner", this);
throw exception;
}
return 0;
}
public abstract void handleError();
}
[Serializable]
private class ParserA : Parser
{
public override void handleError()
{
Console.WriteLine("Handled in A");
}
}
[Serializable]
private class ParserB : Parser
{
public override void handleError()
{
Console.WriteLine("Handled in B");
}
}
static void Main(String[] args)
{
try
{
int x = new ParserA().GetData();
int y = new ParserB().GetData();
}
catch (InvalidDataException ex)
{
Parser parser = ex.Data["owner"] as Parser;
if(parser != null)
parser.handleError();
// or even this if you prefer:
if (parser is ParserA)
Console.WriteLine("A");
}
}
}
In a real case scenario GetData method would be virtual, but you got the idea
I have implemented a generic method for calling function in my web service methods and catching exceptions. The idea is to centralize the exception handling and log why the exception occured:
public class WebServiceHandler
{
public T Execute<T>(Func<T> body)
{
//wrap everything in common try/catch
try
{
return body();
}
catch (SoapException)
{
//rethrow any pre-generated SOAP faults
throw;
}
catch (Exception ex)
{
Logger.AddTextToLog(Logger.LogLevel.Error, "An error occured");
var innerExceptionMessage = ex.InnerException != null ? ex.InnerException.Message : "";
throw GenerateSoapException(
ex.Message,
innerExceptionMessage,
SoapException.ServerFaultCode);
}
}
}
I'm using the method like this in my web service methods:
[WebMethod]
GetXXX(string param1, string param2)
{
var request = new GetXXXRequest(param1, param2);
return _webServiceHandler.Execute(() => _controller.GetXXX(request));
}
[WebMethod]
GetYYY(string param1)
{
var request = new GetYYYRequest(param1);
return _webServiceHandler.Execute(() => _controller.GetYYY(request));
}
In case of exceptions I would like to log the parameter names and values used as input to the controller.GetXXX(request) and controller.GetYYY(request) methods in the Execute method.
How can I achieve this?
Or is there a better way to achieve the same goal?
I did not tried with expressions but I have a solution for delegates;
public static List<object> GetMethodParameterValues(Delegate method)
{
var target = method.Target;
if (target == null) return null;
var fields = target.GetType().GetFields();
var valueList = fields.Select(field => field.GetValue(target)).ToList();
return valueList;
}
First of all you can't query the runtime for the that information magically, implicitly.
It is true that you can implicitly learn the complete identity of the currently executing method, its caller, its caller's caller and all of the stacktrace (except for the particular generic arguments in case of generic methods) by instantiating the StackTrace class or by calling the MethodBase.GetCurrentMethod() method.
It is also true that the resulting MethodBase instances hold information about the methods' parameters and so, you might be able to learn the names of the parameters, but that's where it all ends. If a mechanism allowing you to implicitly probe for parameter or local variable values would've been invented then everything would be a lot slower.
What you can do, is help yourself, a bit, by means of the Expression<Lambda> class and its entourage. It's gonna be a bit slow, but you get to choose whether you want reviewable and easy to manage code, or misteriously hard to manage and very very fast code.
Expression<Lambda> is how LINQ manages to not do a full table scan of database tables but rather understand what you did with your query and translate that (at runtime) into SQL or whatever other language you might imagine (depending on the actual LINQ provider).
First of all, I would suggest splitting concerns into 2 categories:
Retrieving names and values (as implicitly as possible)
Using names and values (wherever you want to)
To make that happen you need to think about an entity which can hold the results of point 1. In my suggestion to you that would be a kind of Dictionary<string, object> but you can do whatever suits you best.
My suggestion can be used like so:
public void SomeMethod(string x, int y) {
IDictionary<string, object> paramValues = Helper.TapInto(
() => x,
() => y
);
// paramValues["x"] an paramValues["y"] will hold the values of x and y
}
So, on to the coding bit. You could write a Helper class like so:
public static class Helper {
}
In that Helper class you could invent a static method ( I called mine TapInto maybe that's not the best name for it ) which receives a primitive array of Expression<Func<object>> instances. It does that with a params modifier so that you can easily pass implicit lambdas to it. As a return it gives you a hashtable from string to object representing the "decompiled" variable names and their associated values.
In my case, I also created a private overload of that same method which is actually an "extension" method, to make the code clearer.
public static class Helper {
// ... an overload of the TapInto method is about to appear right here
public static IDictionary<string, object> TapInto(params Expression<Func<object>>[] parameterTouchers) {
var result = new Dictionary<string, object>();
foreach (var toucher in parameterTouchers) {
string name;
object value;
toucher.TapInto(out name, out value);
result[name] = value;
}
return result;
}
So all the public method does is iterate through the list and accumulate the yielded results into the dictionary.
Next let's look at the real magic, which happens in the toucher.TapInto(out name, out value) call:
public static class Helper {
private static void TapInto(this Expression<Func<object>> #this, out string name, out object value) {
Expression expression = #this.Body;
if (expression is UnaryExpression)
expression = (expression as UnaryExpression).Operand;
name = (expression as MemberExpression).Member.Name;
Func<object> compiledLambda = #this.Compile();
value = compiledLambda();
}
// ... public helper method right here
}
What we're doing here is "we're looking inside" the lambda with a magnifying glass.
Because we're gonna use stuff other than object variables it's imminent to observe an implicit conversion like
.. int someParameter ..
object obj = someParameter;
which is only implicit in the actual C# code, but is actually compiled as an explicit conversion:
object obj = (object)someParameter;
But you might have a plain object parameter, like object anotherParam, in which case there would be no conversion at all.
That is why, upon observing the expression's intricate details, I presume I might find a conversion ( represented by the UnaryExpression class ) or not.
Actually it's like saying: In this particular case, my contract to the calling code is that it may send me only stuff which falls into these 2 categories:
Immediate object variable reference: () => someObjectVariable
Variable reference with a conversion: () => (object)x
The contract also accidentally states that the "conversion" bit can be replaced by a UnaryExpression, for instance: () => !someBool.
It also states that you cannot do something like:
() => 123
or () => a + b + c + 100
or anything else in those directions
So, to wrap it up:
You could write your nice little helper
You could use it wherever you want to use it to produce maps between param names and their values although it's not 100% implicit, but at least it won't compile if you rename a parameter without complete refactoring or it will let you rename the parameter references if you choose to rename the parameters using refactoring
(it also works on fields, local variables, etc)
Pass your dictionaries in between parts of your code that are interested in them and use them accordingly !
You can use another parameter to the Execute method to get callback from function.
An Action or your result type. In this case string for example:
public T Execute<T>(Func<T> body, Action<string> callback)
{
//wrap everything in common try/catch
try
{
//do stuff
callback("results are...");
}
You can also use a delegate:
public void CallbackDelegate( string str );
public T Execute<T>(Func<T> body, CallbackDelegate callback)
{
//wrap everything in common try/catch
try
{
//do stuff
callback("results are...");
}
Maybe something like this:
sealed class Param
{
public string Name
{
get;
private set;
}
public object Value
{
get;
private set;
}
public Param(string name, object value)
{
Name = name;
Value = value;
}
}
public T Execute<T>(Func<T> body, params Param[] parameters)
{
//wrap everything in common try/catch
try
{
return body();
}
catch (SoapException)
{
//rethrow any pre-generated SOAP faults
throw;
}
catch (Exception ex)
{
Logger.AddTextToLog(Logger.LogLevel.Error, "An error occured");
foreach (var parameter in parameters)
{
Logger.AddTextToLog(
Logger.LogLevel.Error,
string.Format(
"{0} : {1}",
parameter.Name,
parameter.Value ?? "null"));
}
var innerExceptionMessage = ex.InnerException != null ? ex.InnerException.Message : "";
throw GenerateSoapException(
ex.Message,
innerExceptionMessage,
SoapException.ServerFaultCode);
}
}
And then you call:
[WebMethod]
GetXXX(string param1, string param2)
{
var request = new GetXXXRequest(param1, param2);
return _webServiceHandler.Execute(() => _controller.GetXXX(request)
new Parameter("param1", param1),
new Parameter("param2", param2));
}
What I ended up doing was to add the request class as parameter to the Execute method like this:
public T Execute<T, TU>(Func<T> body, TU parameterClass) where TU : class
{
//wrap everything in common try/catch
try
{
return body();
}
catch (SoapException)
{
//rethrow any pre-generated SOAP faults
throw;
}
catch (Exception ex)
{
var serializedObject = ParameterUtil.GetPropertyNamesAndValues(parameterClass);
Logger.AddTextToLog(Logger.LogLevel.Error, string.Format("An error occured when calling braArkiv Web Services. Web service method arguments: {0}", serializedObject), ex);
var innerExceptionMessage = ex.InnerException != null ? ex.InnerException.Message : "";
throw GenerateSoapException(
ex.Message,
innerExceptionMessage,
SoapException.ServerFaultCode);
}
}
public static class ParameterUtil
{
public static string GetPropertyNamesAndValues<T>(T o) where T : class
{
using (var stringWriter = new StringWriter())
{
var xmlSerializer = new XmlSerializer(o.GetType());
xmlSerializer.Serialize(stringWriter, o);
stringWriter.Close();
return stringWriter.ToString();
}
}
}
Usage:
[WebMethod]
GetXXX(string param1, string param2)
{
var request = new GetXXXRequest(param1, param2);
return _webServiceHandler.Execute(() => _controller.GetXXX(request), request);
}
When an exception occurs I just serialize the parameterClass parameter containing the web service method parameters and add the string to my log.
Thank you all for the constructive input to my question!
I have multiple calls to methods in a 3rd party library.
These methods have a wide variety of different signatures / parameter combinations.
There are specific errors that the 3rd party library generates that I would like to catch and handle, and since the resolution is the same I would like it to take place in a single handler.
So I want to be able to have a method that essentially takes in as parameters a function (delegate) and its arguments, and invokes it inside some try/catch logic.
I don't know if its just not possible or I'm not getting the syntax right, but I can't figure out how to handle the fact that the signature for each method being passed in is different.
Any suggestions?
You could use Action and Func<T> to wrap the methods, and closures to pass arguments.
public TResult CallMethod<TResult>(Func<ThirdPartyClass, TResult> func)
{
try
{
return func(this.wrappedObject);
}
catch(ThirdPartyException e)
{
// Handle
}
}
public void CallMethod(Action<ThirdPartyClass> method)
{
this.CallMethod(() => { method(this.WrappedObject); return 0; });
}
You could then use this via:
var result = wrapper.CallMethod(thirdParty => thirdParty.Foo(bar, baz));
Edit: The above was assuming you were wrapping an instance of the third party library. Given (from your comments) that these are static methods, you can just use:
public static TResult CallMethod<TResult>(Func<TResult> func)
{
try
{
return func();
}
catch(ThirdPartyException e)
{
// Handle
}
}
public static void CallMethod(Action method)
{
CallMethod(() => { method(); return 0; });
}
And then call via:
var result = Wrapper.CallMethod(() => ThirdParty.Foo(bar, baz));
There are a few different common patterns for returning the result of a function call in public APIs. It is not obvious which is the best approach. Is there a general consensus on a best practice, or, at least convincing reasons why one pattern is better the others?
Update By public API, I mean the public members that are exposed to dependent assemblies. I am not referring exclusively to an API that is exposed publicly as a web service. We can make the assumption that clients are using .NET.
I wrote a sample class below to illustrate the different patterns for returning values, and I have annotated them expressing my concerns for each one.
This is a bit of a long question, but I'm sure I'm not the only person to have considered this and hopefully this question will be interesting to others.
public class PublicApi<T> // I am using the class constraint on T, because
where T: class // I already understand that using out parameters
{ // on ValueTypes is discouraged (http://msdn.microsoft.com/en-us/library/ms182131.aspx)
private readonly Func<object, bool> _validate;
private readonly Func<object, T> _getMethod;
public PublicApi(Func<object,bool> validate, Func<object,T> getMethod)
{
if(validate== null)
{
throw new ArgumentNullException("validate");
}
if(getMethod== null)
{
throw new ArgumentNullException("getMethod");
}
_validate = validate;
_getMethod = getMethod;
}
// This is the most intuitive signature, but it is unclear
// if the function worked as intended, so the caller has to
// validate that the function worked, which can complicates
// the client's code, and possibly cause code repetition if
// the validation occurs from within the API's method call.
// It also may be unclear to the client whether or not this
// method will cause exceptions.
public T Get(object argument)
{
if(_validate(argument))
{
return _getMethod(argument);
}
throw new InvalidOperationException("Invalid argument.");
}
// This fixes some of the problems in the previous method, but
// introduces an out parameter, which can be controversial.
// It also seems to imply that the method will not every throw
// an exception, and I'm not certain in what conditions that
// implication is a good idea.
public bool TryGet(object argument, out T entity)
{
if(_validate(argument))
{
entity = _getMethod(argument);
return true;
}
entity = null;
return false;
}
// This is like the last one, but introduces a second out parameter to make
// any potential exceptions explicit.
public bool TryGet(object argument, out T entity, out Exception exception)
{
try
{
if (_validate(argument))
{
entity = _getMethod(argument);
exception = null;
return true;
}
entity = null;
exception = null; // It doesn't seem appropriate to throw an exception here
return false;
}
catch(Exception ex)
{
entity = null;
exception = ex;
return false;
}
}
// The idea here is the same as the "bool TryGet(object argument, out T entity)"
// method, but because of the Tuple class does not rely on an out parameter.
public Tuple<T,bool> GetTuple(object argument)
{
//equivalent to:
T entity;
bool success = this.TryGet(argument, out entity);
return Tuple.Create(entity, success);
}
// The same as the last but with an explicit exception
public Tuple<T,bool,Exception> GetTupleWithException(object argument)
{
//equivalent to:
T entity;
Exception exception;
bool success = this.TryGet(argument, out entity, out exception);
return Tuple.Create(entity, success, exception);
}
// A pattern I end up using is to have a generic result class
// My concern is that this may be "over-engineering" a simple
// method call. I put the interface and sample implementation below
public IResult<T> GetResult(object argument)
{
//equivalent to:
var tuple = this.GetTupleWithException(argument);
return new ApiResult<T>(tuple.Item1, tuple.Item2, tuple.Item3);
}
}
// the result interface
public interface IResult<T>
{
bool Success { get; }
T ReturnValue { get; }
Exception Exception { get; }
}
// a sample result implementation
public class ApiResult<T> : IResult<T>
{
private readonly bool _success;
private readonly T _returnValue;
private readonly Exception _exception;
public ApiResult(T returnValue, bool success, Exception exception)
{
_returnValue = returnValue;
_success = success;
_exception = exception;
}
public bool Success
{
get { return _success; }
}
public T ReturnValue
{
get { return _returnValue; }
}
public Exception Exception
{
get { return _exception; }
}
}
Get - use this if validation failing is unexpected or if it's feasible for callers to validate the argument themselves before calling the method.
TryGet - use this if validation failing is expected. The TryXXX pattern can be assumed to be familiar due it's common use in the .NET Framework (e.g., Int32.TryParse or Dictonary<TKey, TValue>.TryGetValue).
TryGet with out Exception - an exception likely indicates a bug in the code passed as delegates to the class, because if the argument was invalid then _validate would return false instead of throwing an exception and _getMethod would not be called.
GetTuple, GetTupleWithException - never seen these before. I wouldn't recommend them, because a Tuple isn't self-explaining and thus not a good choice for a public interface.
GetResult - use this if _validate needs to return more information than a simple bool. I wouldn't use it to wrap exceptions (see: TryGet with out Exception).
If by "public API" you mean an API by will be consumed by applications outside of your control and those client apps will written in a variety of languages/platforms I would suggest returning very basic types (e.g. strings, integers, decimals) and use something like JSON for more complex types.
I don't think you can expose a generic class in a public API since you don't know if the client will support generics.
Pattern-wise I would lean towards a REST-like approach rather than SOAP. Martin Fowler has a good article high level article on what this means: http://martinfowler.com/articles/richardsonMaturityModel.html
Things to consider, before answer:
1- There is a special situation about DOTNet programming languages & Java, due that you can easily retrieve objects, instead of only primitive types. Example: so a "plain C" A.P.I. may differ to a C# A.P.I.
2- If there is an error in you A.P.I., while retriving a data, how to handle, without interrumpting you application.
Answer:
A pattern, I have seen in several libraries, its a function, that its main result its an integer, in which 0 means "success", and another integer value means an specific error code.
The function may have several arguments, mostly read-only or input parameters, and a single reference or out parameter that maybe a primitive type a reference to an object, that maybe changed, or a pointer to an object or data structure.
In case of exceptions, some developers, may catch them and generate an specific error code.
public static class MyClass
{
// not recomended:
int static GetParams(ref thisObject, object Param1, object Params, object Param99)
{
const int ERROR_NONE = 0;
try
{
...
}
catch (System.DivideByZeroException dbz)
{
ERROR_NONE = ...;
return ERROR_NONE;
}
catch (AnotherException dbz)
{
ERROR_NONE = ...;
return ERROR_NONE;
}
return ERROR_NONE;
} // method
// recomended:
int static Get(ref thisObject, object ParamsGroup)
{
const int ERROR_NONE = 0;
try
{
...
}
catch (System.DivideByZeroException dbz)
{
ERROR_NONE = ...;
return ERROR_NONE;
}
catch (AnotherException dbz)
{
ErrorCode = ...;
return ERROR_NONE;
}
return ERROR_NONE;
} // method
} // class
Its similar to your tuple result. Cheers.
UPDATE 1: Mention about exception handling.
UPDATE 2: explicit declare constants.