3
\$\begingroup\$

I have multiple services with a lot of methods. Every method returns an int as a result code. All methods have 2 types of signatures:

  1. Only with IN params
  2. n of IN params and m OUT params in the end (in most cases 1 OUT param)

Up until now I came up with this solution:

#region Decorators
#region [Getters]
#region [Void]
protected delegate int KeeperAPIDelegateVoid<T1>(string sessionID, T1 obj1);
protected delegate int KeeperAPIDelegateVoid<T1, T2>(string sessionID, T1 obj1, T2 obj2);
protected void DataGrabber<T1>(KeeperAPIDelegateVoid<T1> action, params object[] args)
{
 int result = action.Invoke(SessionID, (T1)args[0]);
 if (result != 0) throw HandleError(result);
}
protected void DataGrabber<T1, T2>(KeeperAPIDelegateVoid<T1, T2> action, params object[] args)
{
 int result = action.Invoke(SessionID, (T1)args[0], (T2)args[1]);
 if (result != 0) throw HandleError(result);
}
#endregion
#region [Typed]
protected delegate int KeeperAPIDelegate<TReturn>(string sessionID, out TReturn rObj);
protected delegate int KeeperAPIDelegate<T1, TReturn>(string sessionID, T1 obj1, out TReturn rObj);
protected delegate int KeeperAPIDelegate<T1, T2, TReturn>(string sessionID, T1 obj1, T2 obj2, out TReturn rObj);
protected delegate int KeeperAPIDelegate<T1, T2, T3, TReturn>(string sessionID, T1 obj1, T2 obj2, T3 obj3, out TReturn rObj);
protected delegate int KeeperAPIDelegate<T1, T2, T3, T4, T5, TReturn>(string sessionID, T1 obj1, T2 obj2, T3 obj3, T4 obj4, T5 obj5, out TReturn rObj);
protected delegate int KeeperAPIDelegate<T1, T2, T3, T4, T5, T6, TReturn1, TReturn2, TReturn3>(string sessionID, T1 obj1, T2 obj2, T3 obj3, T4 obj4, T5 obj5, T6 obj6, out TReturn1 rObj1, out TReturn2 rObj2, out TReturn3 rObj3);
protected TReturn DataGrabber<TReturn>(KeeperAPIDelegate<TReturn> action)
{
 TReturn data;
 int result = action.Invoke(SessionID, out data);
 if (result == 0) return data;
 throw HandleError(result);
}
protected TReturn DataGrabber<T1, TReturn>(KeeperAPIDelegate<T1, TReturn> action, params object[] args)
{
 TReturn data;
 int result = action.Invoke(SessionID, (T1)args[0], out data);
 if (result == 0) return data;
 throw HandleError(result);
}
protected TReturn DataGrabber<T1, T2, TReturn>(KeeperAPIDelegate<T1, T2, TReturn> action, params object[] args)
{
 TReturn data;
 int result = action.Invoke(SessionID, (T1)args[0], (T2)args[1], out data);
 if (result == 0) return data;
 throw HandleError(result);
}
protected TReturn DataGrabber<T1, T2, T3, TReturn>(KeeperAPIDelegate<T1, T2, T3, TReturn> action, params object[] args)
{
 TReturn data;
 int result = action.Invoke(SessionID, (T1)args[0], (T2)args[1], (T3)args[2], out data);
 if (result == 0) return data;
 throw HandleError(result);
}
protected TReturn DataGrabber<T1, T2, T3, T4, T5, TReturn>(KeeperAPIDelegate<T1, T2, T3, T4, T5, TReturn> action, params object[] args)
{
 TReturn data;
 int result = action.Invoke(SessionID, (T1)args[0], (T2)args[1], (T3)args[2], (T4)args[3], (T5)args[4], out data);
 if (result == 0) return data;
 throw HandleError(result);
}
protected Tuple<TReturn1, TReturn2, TReturn3> DataGrabber<T1, T2, T3, T4, T5, T6, TReturn1, TReturn2, TReturn3>(KeeperAPIDelegate<T1, T2, T3, T4, T5, T6, TReturn1, TReturn2, TReturn3> action, params object[] args)
{
 TReturn1 data1;
 TReturn2 data2;
 TReturn3 data3;
 int result = action.Invoke(SessionID, (T1)args[0], (T2)args[1], (T3)args[2], (T4)args[3], (T5)args[4], (T6)args[5], out data1, out data2, out data3);
 if (result == 0) return new Tuple<TReturn1, TReturn2, TReturn3>(data1, data2, data3);
 throw HandleError(result);
}
#endregion
#endregion

I call my methods like:

public SelectOptionDesc[] GetContractorFieldOptions(long contractorId, long fieldId)
{
 return DataGrabber<long, long, SelectOptionDesc[]>(TelepayClient.GetTelepayContractorFieldSelectOptions, new object[] { contractorId, fieldId });
}

Is it an OK solution or is there a better way? Modifying services is not an option, because it's a 3rd party service and I don't have access.

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Aug 10, 2016 at 7:58
\$\endgroup\$
6
  • 1
    \$\begingroup\$ You probably should have a look at Aspect Oriented Programming. There are a bunch of AOP frameworks (such as postsharp) around which let you do this kind of decoration with a lot less coding work and in a much more generic fashion. \$\endgroup\$ Commented Aug 10, 2016 at 8:39
  • \$\begingroup\$ Thx. I thought about this. But mostly about T4 templates. PostSharp - interesting idea. Thx! \$\endgroup\$ Commented Aug 10, 2016 at 8:40
  • \$\begingroup\$ Is it correct, that the decorator only checks the error code and throws the corresponding exception if needed or is it just a simplified example? \$\endgroup\$ Commented Aug 10, 2016 at 9:10
  • \$\begingroup\$ @JanDotNet, it's checking and processing the error (ok) code, but it should return value as well. It's Ctrl+C, Ctrl+V code. \$\endgroup\$ Commented Aug 10, 2016 at 9:14
  • \$\begingroup\$ @Kindzoku: Ok, then it should be possible to throw the exception direct in the service method (instead of returning an error code). That way, you could call the service methods directly and remove the whole decorator code, right? That would simplify the code dramatically. \$\endgroup\$ Commented Aug 10, 2016 at 10:02

1 Answer 1

2
\$\begingroup\$

If the service methods are not changeable, I don't see a solution that is fundamentally better then yours...

  • If there are only one or a few methods with 3 return values, consider to create more descriptive custom types for the return values (instead of using Tuple<T1, T2, T3>).
  • The check for (result == 0) is implemented for each method. You could do that within the method HandleError. However, that requires, that HandleError does not return the exception but throws it directly if result is != 0.
answered Aug 10, 2016 at 13:11
\$\endgroup\$
3
  • \$\begingroup\$ As far as I know, out parameter in Func<> is return value, not actual out property. So no, unfortunately I can't use Func. And Action is void, so no use for me too. \$\endgroup\$ Commented Aug 10, 2016 at 13:23
  • \$\begingroup\$ right....I'll remove that point from my answer ;). \$\endgroup\$ Commented Aug 10, 2016 at 13:26
  • \$\begingroup\$ 1st point - not sure if I need them more descriptive + I haven't seen all methods yet ;D. 2nd point - a good one. Thx. \$\endgroup\$ Commented Aug 10, 2016 at 13:28

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.