Background: I have an old class (represented as OriginalComponent in the code below) which I would like to add logging to its methods. The project employs neither an IOC container nor AOP capabilities, and as such I wanted to be cleaver about how I add Logging.
My thought for solution: I feel that a good place to start is the dynamic construct that will act as a decorator class. My code is listed below.
However, my code suffers from a big flaw:
If I do not call the methods in my OriginalComponent class with precise parameter types then the decorator class will not find the methods.
Question:
Does anyone know how I can overcome this shortcoming and ultimately allow me to be able to call a method in a similar inexact way that the compiler allows for an inexact parameter types in a method call.
The following code is written in LinqPad 5.
void Main()
{
var calculator = new OriginalComponent();
var logCalc = new DecoratorLogging<IComponent, LogEnableAttribute>(calculator);
dynamic d = logCalc;
// Does not work
try
{
var ri = d.Add(1, 2);
Console.WriteLine($"Main: ri: {ri.GetType().Name}::{ri}");
Console.WriteLine(new string('-', 20));
Console.WriteLine();
}
catch (Exception ex)
{
Console.WriteLine("ri = d.Add(1, 2) failed");
Console.WriteLine(ex.ExceptionMessages());
Console.WriteLine(new string('=', 60));
Console.WriteLine();
}
// Works...
try
{
var ri = d.Add(1M, 2M);
Console.WriteLine($"Main: ri: {ri.GetType().Name}::{ri}");
Console.WriteLine(new string('-', 20));
Console.WriteLine();
}
catch (Exception ex)
{
Console.WriteLine(ex.ExceptionMessages());
Console.WriteLine(new string('=', 60));
Console.WriteLine();
}
}
// Define other methods and classes here
///// Common interface
public interface IComponent { }
///// OriginalComponent
public class OriginalComponent : IComponent
{
[LogEnable(true)]
public decimal Add(decimal a, decimal b) => a + b;
}
///// DecoratorLogging<TComponent, TAttr>
public class DecoratorLogging<TComponent, TAttr> : DynamicObject, IComponent
where TComponent : class
where TAttr : Attribute, IAttributeEnabled
{
private TComponent _orig;
public DecoratorLogging(TComponent orig = null) { _orig = orig; }
public TComponent Orig => _orig;
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
var methNm = binder.Name;
var parmType = args.Select(a => a.GetType()).ToArray();
MethodInfo methodInfo = null;
dynamic tyObj = this;
for (int i = 0; methodInfo == null; ++i)
{
try { tyObj = tyObj.Orig; }
catch (RuntimeBinderException) { throw new Exception($"Method {methNm} was not found."); }
var ty = tyObj.GetType();
methodInfo = ty.GetMethod(methNm, parmType);
}
result = null;
Stopwatch sw = null;
try
{
BeforeLoggingConcern(binder, args, methodInfo, out sw);
if (tyObj.GetType() == _orig.GetType())
result = methodInfo.Invoke(_orig, args);
else
((dynamic)_orig).TryInvokeMember(binder, args, out result);
AfterLoggingConcern(binder, result, methodInfo, sw);
}
catch (Exception ex)
{
ExceptionLoggingConcern(binder, ex, methodInfo, sw);
throw;
}
return true;
}
private void BeforeLoggingConcern(InvokeMemberBinder binder, object[] args, MethodInfo mi, out Stopwatch sw)
{
sw = null;
var isEnabled = IsLogEnabled(mi);
if (!isEnabled) return;
Console.WriteLine($"Logging Before: {binder.Name} method was called");
var sArgs = string.Join(", ", args.Select(a => $"({a.GetType().Name}, {a})").ToArray());
Console.WriteLine("Logging Aguments: {0}", sArgs);
Console.WriteLine();
sw = new Stopwatch();
sw.Start();
}
private void ExceptionLoggingConcern(InvokeMemberBinder binder, Exception ex, MethodInfo mi, Stopwatch sw)
{
var isEnabled = IsLogEnabled(mi);
if (!isEnabled) return;
if (sw != null)
{
sw.Stop();
Console.WriteLine($"Logging Exception: {binder.Name} threw an exception after {sw.ElapsedMilliseconds} milliseconds");
}
else
Console.WriteLine($"Logging Exception: {binder.Name} threw an exception");
Console.WriteLine($"Logging Internal message:{Environment.NewLine}\t{ex.ExceptionMessages()}");
Console.WriteLine(new string('*', 10));
Console.WriteLine();
}
private void AfterLoggingConcern(InvokeMemberBinder binder, object result, MethodInfo mi, Stopwatch sw)
{
var isEnabled = IsLogEnabled(mi);
if (!isEnabled) return;
if (sw != null)
{
sw.Stop();
Console.WriteLine($"Logging After: {binder.Name} ended after {sw.ElapsedMilliseconds} milliseconds");
}
else
Console.WriteLine($"Logging After: {binder.Name} ended");
Console.WriteLine($"Logging resulting in: type: {result.GetType().Name}, value: {result}");
Console.WriteLine(new string('.', 10));
Console.WriteLine();
}
private bool IsLogEnabled(MethodInfo method)
{
var logAttrs = method.GetCustomAttributes<TAttr>(true);
if (logAttrs == null) return false;
return logAttrs.Any(a => a.IsEnabled);
}
}
///// IAttributeEnabled
public interface IAttributeEnabled
{
bool IsEnabled { get; }
}
///// LogEnableAttribute
[AttributeUsage(AttributeTargets.Method, Inherited = true)]
public class LogEnableAttribute : Attribute, IAttributeEnabled
{
private bool _logEnabled;
public LogEnableAttribute(bool logEnabled = true) { _logEnabled = logEnabled; }
public bool IsEnabled => _logEnabled;
}
///// Ext (Used to extract all exception messages)
public static class Ext
{
public static string ExceptionMessages(this Exception ex)
{
if (ex.InnerException == null) return ex.Message;
return string.Join($"{Environment.NewLine}\t", ex.InnerExceptions().Select((a, i) => $"{i}. {a.Message}"));
}
public static IEnumerable<Exception> InnerExceptions(this Exception ex)
{
for (Exception ix = ex; ix != null; ix = ix.InnerException)
yield return ix;
}
}
The problem with my code above is attempting to find the method using types that do not exist in the OriginalComponent class. The line:
methodInfo = ty.GetMethod(methNm, parmType);
is the culprit.
What I needed to do is use the Type1.IsAssignableFrom(Type2) to resolve which method to call.
Related
I am trying out this piece of code below from:
Is there a way to check if a file is in use?
But, it gives error:
The type arguments for method TimeoutFileAction(Func) cannot be inferred from the usage.
Any idea how to fix this?
TimeoutFileAction(() => { System.IO.File.etc...; return null; } );
Reusable method that times out after 2 seconds
private T TimeoutFileAction<T>(Func<T> func)
{
var started = DateTime.UtcNow;
while ((DateTime.UtcNow - started).TotalMilliseconds < 2000)
{
try
{
return func();
}
catch (System.IO.IOException exception)
{
//ignore, or log somewhere if you want to
}
}
return default(T);
}
You MUST have an output other than Type of void.
When you do this: () => { System.IO.File.etc...; return null; } the output type is void and you cannot have that for a Func<T>. If you want a Void Type then use Action.
If you want both void and T, then just write an overflow method. Se Code below:
public static void Main()
{
var today = new DateTime(2021, 10, 25, 5, 40, 0);
Console.WriteLine(today.AddHours(7).AddMinutes(36));
TimeoutFileAction(() => { Test(); });
TimeoutFileAction(Test);
}
private static string Test() => "Test";
private static void TimeoutFileAction(Action func)
{
var started = DateTime.UtcNow;
while ((DateTime.UtcNow - started).TotalMilliseconds < 2000)
{
try
{
func();
}
catch (IOException exception)
{
//ignore, or log somewhere if you want to
}
}
}
private static T TimeoutFileAction<T>(Func<T> func)
{
var started = DateTime.UtcNow;
while ((DateTime.UtcNow - started).TotalMilliseconds < 2000)
{
try
{
return func();
}
catch (IOException exception)
{
//ignore, or log somewhere if you want to
}
}
return default(T);
}
Storing the generic class T in a variable and reusing it in sub methode.
For a WebService with crud on few object:
Foo: Bar: Etc..
SetFoo SetBar SetEtc
GetFoo GetBar GetEtc
UpdateFoo UpdateBar UpdateEtc
DeleteFoo DeleteBar DeleteEtc
GetList .. ..
GetPending .. ..
Processed .. ..
I have the following singleton generic wrapper on client side, with methode like:
public bool Get<T>(int i, out DloExtention result)
// DloExtention is an interface implemented by foo, bar, etc..
{
result = null;
try
{
if (typeof(T) == typeof(Foo))
{
result = WebserviceClient.GetFoo(i);
}
else if (typeof(T) == typeof(Bar))
{
result = WebserviceClient.GetBar(i);
}
else if (typeof(T) == typeof(Etc))
{
result = WebserviceClient.GetEtc(i);
}
else
{
throw new NotSupportedException("Get<T>, T is not a supported type.");
}
}
catch (Exception ex)
{
Log4N.Logger.Error($"Error in Namespace.ClientSide.Get<{nameof(T)}>(int {i} ). " + ex.Message);
return false;
}
return true;
}
So I can handle all the type simply with the same generic object:
class Processor
{
HashSet<int> validOperation = new HashSet<int>();
HashSet<int> invalidOperation = new HashSet<int>();
internal void Run<T>()
{
if (Wrapper.Instance.GetListPending<T>(out int[] newEntityList) && newEntityList.Any())
{
ProcessEntities<T>(newEntityList, false);
}
}
private void ProcessEntities<T>(int[] idsEnt, bool singleMode)
{
foreach (var idEnt in idsEnt)
{
ProcessEntity<T>(idEnt, false);
}
CloseValidOperation();
RemoveInvalidOperation();
}
internal void ProcessIncident<T>(int idEnt)
{
if (Wrapper.Instance.Get<T>(idEnt, out LanDataExchangeCore.LanDataExchangeWCF.DloExtention currentEntity))
{
if (currentEntity.isValid() && currentEntity.toLocalDB())
{
validOperation.Add(idEnt);
}
else
{
invalidOperation.Add(idEnt);
}
}
}
Only Wrapper.Instance.Get<T> and Wrapper.Instance.GetListPending<T> needs the generic parameter.
But every methode in the way need to use it only to be able to deliver <T> to the last methode.
Is there a way to save the <T> in the Run<T> call into a private variable so inner methode of the class can use it ?
I have try adding a Type myType; but can't find the way to use it in generic call. Exemple for the Wrapper.Instance.Get<T>
Type myType; // class property
var fooWrapperGet = typeof(Wrapper).GetMethod("Get");
var fooOfMyTypeMethod = fooWrapperGet.MakeGenericMethod(new[] { myType });
//fooOfMyTypeMethod.Invoke(Wrapper.Instance , new object[] { new myType() });
// fooWrapperGet, as my wrapper is a singleton, Wrapper dont exposed Get<T>, but Wrapper.instance will expose it.
// new myType() <- do not compile.
Does this work for you?
private Dictionary<Type, Func<int, DloExtention>> gets =
new Dictionary<System.Type, Func<int, DloExtention>>()
{
{ typeof(Foo), WebserviceClient.GetFoo },
{ typeof(Bar), WebserviceClient.GetBar },
{ typeof(Etc), WebserviceClient.GetEtc },
};
public bool Get<T>(int i, out DloExtention result)
{
result = null;
var flag = false;
if (gets.ContainsKey(typeof(T)))
{
result = gets[typeof(T)](i);
flag = true;
}
return flag;
}
The beauty of this is that you can populate your dictionary at run-time.
I am creating a new Asp.Net Core solution based on an existing Asp.Net 4.5 solution.
The current solution uses Microsoft Unity Container and the Infrastructure has references to the Service Locator.
I want to get rid of the Service Locator and avoid referencing specific DI containers in my new Infrastructure.
I'm having an issue coming up with a good way to replace the current Command/Query/Event Dispatcher without any DI container dependencies.
Here is my Dispatcher class
public class Dispatcher : IDispatcher
{
private const string HandleMethodName = "Handle";
public TResponse Request<TResponse>(IQuery<TResponse> query)
{
Type queryType = query.GetType();
// used for when OperationResult<object> was used
Type operationResultTrueReturnType = typeof(TResponse);
if (operationResultTrueReturnType == typeof(object))
{
operationResultTrueReturnType = queryType.GetInterface(typeof(IQuery<>).Name).GenericTypeArguments[0];
}
Type handlerType = typeof(IQueryHandler<,>).MakeGenericType(query.GetType(), operationResultTrueReturnType);
return ExecuteHandler<TResponse>(handlerType, query, queryType);
}
public OperationResult Submit(ICommand command)
{
Type commandType = command.GetType();
var baseTypeAttribute = (CommandBaseTypeAttribute)commandType.GetCustomAttributes(typeof(CommandBaseTypeAttribute), false).FirstOrDefault();
if (baseTypeAttribute != null)
commandType = baseTypeAttribute.BaseType;
try
{
Type handlerType = typeof(ICommandHandler<>).MakeGenericType(commandType);
return ExecuteHandler<OperationResult>(handlerType, command, commandType);
}
catch (InvalidOperationException ex)
{
return new OperationResult(OperationResultStatus.Failure, ex.Message);
}
}
public OperationResult<TResult> Submit<TResult>(ICommand<TResult> command)
{
Type commandType = command.GetType();
var baseTypeAttribute = (CommandBaseTypeAttribute)commandType.GetCustomAttributes(typeof(CommandBaseTypeAttribute), false).FirstOrDefault();
if (baseTypeAttribute != null)
commandType = baseTypeAttribute.BaseType;
try
{
Type handlerType = typeof(ICommandHandler<,>).MakeGenericType(commandType, typeof(TResult));
return ExecuteHandler<OperationResult<TResult>>(handlerType, command, commandType);
}
catch (InvalidOperationException ex)
{
return new OperationResult<TResult>(OperationResultStatus.Failure, default(TResult), ex.Message);
}
}
public void Raise(IDomainEvent domainEvent)
{
Type domainEventType = domainEvent.GetType();
try
{
Type handlerType = typeof(ICommandHandler<>).MakeGenericType(domainEventType);
ExecuteHandler(handlerType, domainEvent, domainEventType);
}
catch (InvalidOperationException)
{
}
}
private static void ExecuteHandler(Type handlerType, object argument, Type argumentType)
{
object handler = ServiceLocator.Current.GetInstance(handlerType);
if (handler == null)
throw new ConfigurationErrorsException("Handler not registered for type " + argumentType.Name);
try
{
MethodInfo handleMethod = handlerType.GetMethod(HandleMethodName, new[] { argumentType });
handleMethod.Invoke(handler, new[] { argument });
}
catch (TargetInvocationException ex)
{
if (ex.InnerException != null)
throw ex.InnerException;
throw;
}
}
private static TReturnValue ExecuteHandler<TReturnValue>(Type handlerType, object argument, Type argumentType)
{
object handler = ServiceLocator.Current.GetInstance(handlerType);
if (handler == null)
throw new ConfigurationErrorsException("Handler not registered for type " + argumentType.Name);
try
{
MethodInfo handleMethod = handlerType.GetMethod(HandleMethodName, new[] { argumentType });
return (TReturnValue)handleMethod.Invoke(handler, new[] { argument });
}
catch (TargetInvocationException ex)
{
if (ex.InnerException != null)
throw ex.InnerException;
throw;
}
}
}
ExecuteHandler has the ServiceLocator call.
How can I handle this without using it?
I like the suggestion provided in the comments. If you want to abstract away the service locator then have the dispatcher explicitly depend on an abstract service provider (like IServiceProvider) that will be used to do resolutions.
public class Dispatcher : IDispatcher {
private readonly IServiceProvider serviceProvider;
public Dispatcher (IServiceProvider serviceProvider) {
this.serviceProvider = serviceProvider;
}
//...other code removed for brevity
private object GetService(Type serviceType) {
return serviceProvider.GetService(serviceType);
}
private void ExecuteHandler(Type handlerType, object argument, Type argumentType) {
object handler = GetService(handlerType);
if (handler == null)
throw new ConfigurationErrorsException("Handler not registered for type " + argumentType.Name);
try {
MethodInfo handleMethod = handlerType.GetMethod(HandleMethodName, new[] { argumentType });
handleMethod.Invoke(handler, new[] { argument });
} catch (TargetInvocationException ex) {
if (ex.InnerException != null)
throw ex.InnerException;
throw;
}
}
private TReturnValue ExecuteHandler<TReturnValue>(Type handlerType, object argument, Type argumentType) {
object handler = GetService(handlerType);
if (handler == null)
throw new ConfigurationErrorsException("Handler not registered for type " + argumentType.Name);
try {
MethodInfo handleMethod = handlerType.GetMethod(HandleMethodName, new[] { argumentType });
return (TReturnValue)handleMethod.Invoke(handler, new[] { argument });
} catch (TargetInvocationException ex) {
if (ex.InnerException != null)
throw ex.InnerException;
throw;
}
}
}
The dispatcher is now no longer tightly coupled to the service locator anti-pattern and allows for any derived provider to be used. This allows you to avoid referencing specific DI containers.
I added an IServiceProvider to the Dispatcher's constructor.
public class Dispatcher : IDispatcher
{
private const string HandleMethodName = "Handle";
private readonly IServiceProvider _serviceProvider;
public Dispatcher(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public TResponse Request<TResponse>(IQuery<TResponse> query)
{
Type queryType = query.GetType();
// used for when OperationResult<object> was used
Type operationResultTrueReturnType = typeof(TResponse);
if (operationResultTrueReturnType == typeof(object))
{
operationResultTrueReturnType = queryType.GetInterface(typeof(IQuery<>).Name).GenericTypeArguments[0];
}
Type handlerType = typeof(IQueryHandler<,>).MakeGenericType(query.GetType(), operationResultTrueReturnType);
return ExecuteHandler<TResponse>(handlerType, query, queryType);
}
public OperationResult Submit(ICommand command)
{
Type commandType = command.GetType();
var baseTypeAttribute = (CommandBaseTypeAttribute)commandType.GetCustomAttributes(typeof(CommandBaseTypeAttribute), false).FirstOrDefault();
if (baseTypeAttribute != null)
commandType = baseTypeAttribute.BaseType;
try
{
Type handlerType = typeof(ICommandHandler<>).MakeGenericType(commandType);
return ExecuteHandler<OperationResult>(handlerType, command, commandType);
}
catch (InvalidOperationException ex)
{
return new OperationResult(OperationResultStatus.Failure, ex.Message);
}
}
public OperationResult<TResult> Submit<TResult>(ICommand<TResult> command)
{
Type commandType = command.GetType();
var baseTypeAttribute = (CommandBaseTypeAttribute)commandType.GetCustomAttributes(typeof(CommandBaseTypeAttribute), false).FirstOrDefault();
if (baseTypeAttribute != null)
commandType = baseTypeAttribute.BaseType;
try
{
Type handlerType = typeof(ICommandHandler<,>).MakeGenericType(commandType, typeof(TResult));
return ExecuteHandler<OperationResult<TResult>>(handlerType, command, commandType);
}
catch (InvalidOperationException ex)
{
return new OperationResult<TResult>(OperationResultStatus.Failure, default(TResult), ex.Message);
}
}
private TReturnValue ExecuteHandler<TReturnValue>(Type handlerType, object argument, Type argumentType)
{
object handler = _serviceProvider.GetService(handlerType);
if (handler == null)
throw new ArgumentException("Handler not registered for type " + argumentType.Name);
try
{
MethodInfo handleMethod = handlerType.GetMethod(HandleMethodName, new[] { argumentType });
return (TReturnValue)handleMethod.Invoke(handler, new[] { argument });
}
catch (TargetInvocationException ex)
{
if (ex.InnerException != null)
throw ex.InnerException;
throw;
}
}
}
Then, injected it in Startup on the client.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ResolutionDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddMvc();
// Domain Event Handlers
services.AddTransient<IEventHandler<RequestCreatedEvent>, RequestCreatedHandler>();
// Domain Event Dispatcher
services.AddSingleton<IDomainEventDispatcher, DomainEventDispatcher>();
// Units of Work
services.AddTransient<IResolutionUnitOfWork, ResolutionUnitOfWork>();
// Commands and Queries
services.AddTransient<ICommandHandler<CreateRequestCommand, Guid>, CreateRequestHandler>();
// Command and Query Dispatcher
services.AddSingleton<IDispatcher, Dispatcher>();
}
I've read some stuff about the ExpandoObject, and that I can expand it with properties,fields,methods.
//that's how to add a property to an ExpandoObject.
dynamic x = new ExpandoObject();
x.NewProp = string.Empty;
But sometimes, it could be handy to add a property with some "extra-code".
class sample
{
// a sample field.
public string sampleString{get;set}
// a sample property with some "extra code"
private string s;
public string sampleExtraString
{
get{return s;}
set{s=value;Console.WriteLine(s);}
}
}
Now my question is, how can I add a property to the ExpandoObject that will execute my Console.WriteLine(s); for example on set.
ExpandoObject implements INotifyPropertyChanged, as explained here
(at the bottom of the page)
((INotifyPropertyChanged)x).PropertyChanged +=
new PropertyChangedEventHandler(Expando_PropertyChanged);
x.NewProp = string.Empty;
private static void Expando_PropertyChanged(object sender,
PropertyChangedEventArgs e)
{
Console.WriteLine("{0} has changed.", e.PropertyName);
}
I think a better approach would be using DynamicObject which you can intercept the calls for methods and properties.
This is a simple example, a more robust one would not use reflection to perform set/get operations on the property but rather using reflection.Emit or any compiled operation strategy.
public class Sample
{
public string SampleExtraString { get; set; }
}
public class Factory
{
public class ExtraPropertyObject<T> : DynamicObject
{
private readonly T instance = default(T);
private readonly Type instanceType = null;
public ExtraPropertyObject(T instance) {
this.instance = instance;
instanceType = instance.GetType();
}
public override bool TrySetMember(SetMemberBinder binder, object value) {
PropertyInfo prop = null;
if (binder.Name.Equals("SampleExtraString")) {
Console.WriteLine(value);
}
prop = instanceType.GetProperty(binder.Name);
if (prop != null) {
try {
prop.SetValue(instance, value);
return true;
}
catch (Exception ex) {
}
}
return false;
}
public override bool TryGetMember(GetMemberBinder binder, out object result) {
var prop = instanceType.GetProperty(binder.Name);
if (prop != null) {
try {
result = prop.GetValue(instance);
return true;
}
catch (Exception ex) {
}
}
result = null;
return false;
}
}
public static dynamic CreateInstance<TInstance>() where TInstance : class, new() {
return new ExtraPropertyObject<TInstance>(new TInstance());
}
public static dynamic CreateInstance<TInstance>(TInstance instance) {
return new ExtraPropertyObject<TInstance>(instance);
}
}
class Program
{
static void Main(string[] args) {
var instance = Factory.CreateInstance<Sample>();
instance.SampleExtraString = "value";
Console.WriteLine("Get Operation: {0}", instance.SampleExtraString);
}
}
I know the question sounds a bit wierd. Sorry for that, having difficulties trying to get where i want to and even explain it. To make it simple, i have a method with Func<T> argument. But i do not always pass a parameterless Action to that method, i need varying numbers of parameters and i'm trying to find a way to avoid overloading my method everytime number of parameters needed is increased
Here is my generic class, I need to overload GetInstance method:
public class MethodResult<T>
{
public T Result { get; set; }
public bool IsResulted { get; set; }
public Exception Error { get; set; }
private MethodResult() { }
public static MethodResult<T> GetInstance<T>(Func<T> method)
{
MethodResult<T> obj = new MethodResult<T>();
try
{
obj.Result = method();
obj.IsResulted = true;
obj.Error = null;
}
catch (Exception ex)
{
obj.Result = default(T);
obj.IsResulted = false;
obj.Error = ex;
}
return obj;
}
public static MethodResult<T> GetInstance<T, T1>(Func<T1, T> method, T1 param1)
{
MethodResult<T> obj = new MethodResult<T>();
try
{
obj.Result = method(param1);
obj.IsResulted = true;
obj.Error = null;
}
catch (Exception ex)
{
obj.Result = default(T);
obj.IsResulted = false;
obj.Error = ex;
}
return obj;
}
}
And here is sample showing how i want to make use of it:
public static void Main(string[] args)
{
var x = MethodResult<int>.GetInstance(IntResult, 5);
Console.WriteLine("Result: {0}, IsResulted: {1}, ErrorMessage: {2}", x.Result, x.IsResulted, (x.Error == null ? "null" : x.Error.Message));
var y = MethodResult<string>.GetInstance(SayHello);
Console.WriteLine("Result: {0}, IsResulted: {1}, ErrorMessage: {2}", y.Result, y.IsResulted, (y.Error == null ? "null" : y.Error.Message));
Console.Read();
}
public static int IntResult(int x) { return x + 1; }
public static int IntResult(int x, int y) { return x + y; }
public static string SayHello() { return "Hello world!"; }
In order to be able to use IntResult(int x, int y) i have to overload GetInstance method with signiture:
public static MethodResult<T> GetInstance<T, T1, T2>(Func<T1, T2, T> method, T1 param1, T2 param2)
It's obvious that this will become very time consuming as it's already been annoying. Is there a way to avoid that much overloading ?
Instead of passing the function to call and the parameters along with it, you can pass a parameterless anonymous delegate calling the method you want to point to with parameters you want. Just remove any overloads of GetInstance just keep:
public static MethodResult<T> GetInstance(Func<T> method)
{
MethodResult<T> obj = new MethodResult<T>();
try
{
obj.Result = method();
obj.IsResulted = true;
obj.Error = null;
}
catch (Exception ex)
{
obj.Result = default(T);
obj.IsResulted = false;
obj.Error = ex;
}
return obj;
}
And then call it as following:
int n = 1;
var x = MethodResult<string>.GetInstance(() => SayHello());
var y = MethodResult<string>.GetInstance(() => IntResult(2));
var z = MethodResult<int>.GetInstance(() => IntResult(n, 9));