lightxx
lightxx

Reputation: 1067

Dynamically invoke method

I have a WCF service that exposes a bunch of [OperationContract]s that perform operations on collections of various types.

Now almost every implementation looks like this:

        public void PerformSomeOperation(List<SomeType> someCollection) {
        // store the exceptions that might be thrown during the operation
        var exceptions = new List<Exception>();

        foreach (var item in someCollection) {
            try {
                this.PerformSomeOperation(someParameters);
            }
            catch (Exception exception) {
                exceptions.Add(exception);
            }
        }

        // Throw the exceptions here after the loop completes. 
        if (exceptions.Count > 0) throw new AggregateException(exceptions);
    }

So, while the this.PerformSomeOperation(...) part changes between the actual implementations, the rest of the skeleton remains the same. Hence the idea to dynamically inject the this.PerformSomeOperation(...) part.

What is the most elegant solution to this? Of course I could pass in the method name of the operation to be performed as string in the outer method's parameter list and use reflection to call the method (.GetMethod(...)) but I was hoping for some elegant Lambda or Delegate construct.

Upvotes: 0

Views: 99

Answers (3)

Sergey Kolodiy
Sergey Kolodiy

Reputation: 5909

It looks like the Template method pattern could be handy in this situation.

You define your base class that contains method skeleton as following:

public abstract class AbstractProcessor<T>
{
    public void ProcessData(List<T> data)
    {
        var exceptions = new List<Exception>();

        foreach (var item in data)
        {
            try
            {
                ProcessDataInternal(item);
            }
            catch (Exception exception)
            {
                exceptions.Add(exception);
            }
        }

        if (exceptions.Count > 0) throw new AggregateException(exceptions);
    }

    protected abstract void ProcessDataInternal(T data);
}

Then you should define concrete implementations and override ProcessDataInternal method:

public class ConcreteProcessor<T> : AbstractProcessor<T>
{
    protected override void ProcessDataInternal(T data)
    {
        // This implementation simply writes data to the console.
        Console.WriteLine(data);
    }
}

Client example:

AbstractProcessor<string> processor = new ConcreteProcessor<string>();
processor.ProcessData(new List<string> { "Hello", "World" });

Upvotes: 2

jgauffin
jgauffin

Reputation: 101192

Use a helper method

public void YourServiceMethod()
{
    var collection = GetSomeDate();
    DoWork(collection, item => PerformSomeOperation(someParameters));
}

private void DoWork(List<SomeType> collection, Action<SomeType> itemProcessor)
{
   var exceptions = new List<Exception>();

    foreach (var item in collection) 
    {
        try 
        {
            itemProcessor(someParameters);
        }
        catch (Exception exception) 
        {
            exceptions.Add(exception);
        }
    }

    // Throw the exceptions here after the loop completes. 
    if (exceptions.Count > 0) throw new AggregateException(exceptions);
}

Or use an extension method:

public void YourServiceMethod()
{
    var collection = GetSomeDate();
    collection.DoWork(item => PerformSomeOperation(someParameters));
}

public class ListExtensions
{
    public void DoWork(this List<SomeType> collection, Action<SomeType> itemProcessor)
    {
       var exceptions = new List<Exception>();

        foreach (var item in collection) 
        {
            try 
            {
                itemProcessor(someParameters);
            }
            catch (Exception exception) 
            {
                exceptions.Add(exception);
            }
        }

        // Throw the exceptions here after the loop completes. 
        if (exceptions.Count > 0) throw new AggregateException(exceptions);
    }
}

Throw in some generics (to support all types):

public void YourServiceMethod()
{
    var collection = GetSomeDate();
    collection.ProcessList(item => PerformSomeOperation(someParameters));
}

public class ListExtensions
{
    public void ProcessList<T>(this IEnumerable<T> collection, Action<T> itemProcessor)
    {
       var exceptions = new List<Exception>();

        foreach (var item in collection) 
        {
            try 
            {
                itemProcessor(someParameters);
            }
            catch (Exception exception) 
            {
                exceptions.Add(exception);
            }
        }

        // Throw the exceptions here after the loop completes. 
        if (exceptions.Count > 0) throw new AggregateException(exceptions);
    }
}

Upvotes: 1

Arcturus
Arcturus

Reputation: 27065

Why not use delegates? These are basically function pointers.

So you have your general DoWork method with an Action:

  public void DoWork(Action<List<SomeType>> myDelegate)
  {
       var exceptions = new List<Exception>();

        foreach (var item in someCollection) 
        {
            try {
                myDelegate(someParameters);
            }
            catch (Exception exception) {
                exceptions.Add(exception);
            }
        }

        // Throw the exceptions here after the loop completes. 
        if (exceptions.Count > 0) throw new AggregateException(exceptions);
  }

And then call it with a pointer to your own function PerformSomeOperation

DoWork(PerformSomeOperation);

Now you are able to switch the methods you can use.

DoWork(PerformOtherOperationWithSameSignature)

Upvotes: 3

Related Questions