I have a collection that I first need to filter and then select one out of it, but how the collection should be processed is different depending on some parameters. So I went with 2 delegates, but somehow I should combine them:
delegate IEnumerable<T> FilterDelegate(IEnumerable<T> collection);
delegate T SelectorDelegate(IEnumerable<T> collection, ref T previous);
//Combine above two to this one:
delegate T GetItemDelegate(IEnumerable<T> collection, ref T previous);
I tried something like this but it fails because the delegates aren't of matching types:
static GetItemDelegate CreateDelegate(FilterDelegate filter, SelectorDelegate select)
{
return Delegate.Combine(filter, select) as GetItemDelegate;
}
Is it possible to create a new delegate that invokes the first one and uses the return value as input parameter for the second one? Without the ref
parameter I got it to work with lambdas, but of course I can't use out
or ref
then.
namespace DelegateTest
{
interface INumericValue
{
int Number { get; }
}
class Test : INumericValue
{
public Test(int i)
{
Number = i;
}
public int Number { get; }
}
class Program
{
delegate IEnumerable<T> FilterDelegate<T>(IEnumerable<T> collection) where T : class, INumericValue;
delegate T SelectDelegate<T>(IEnumerable<T> collection, ref T previous) where T : class, INumericValue;
delegate T CombinedDelegate<T>(IEnumerable<T> collection, ref T previous) where T : class, INumericValue;
static void Main()
{
Test previous = new Test(6);
List<Test> collection = new List<Test>();
FilterDelegate<Test> filter = Filter;
SelectDelegate<Test> select = Select;
CombinedDelegate<Test> combined = Delegate.Combine(filter, select) as CombinedDelegate<Test>;
for (int i = 0; i < 10; i++)
collection.Add(new Test(i));
//Expected result Test with Number = 7
Test result = combined(collection, ref previous);
}
static IEnumerable<T> Filter<T>(IEnumerable<T> collection) where T : class, INumericValue
{
return collection.Where(c => c.Number > 3);
}
static T Select<T>(IEnumerable<T> collection, ref T previous) where T : class, INumericValue
{
var previousNumber = previous.Number;
return previous = collection.FirstOrDefault(c => c.Number > previousNumber);
}
}
}
You can do it like this, but not sure it's much cleaner than just chaining invocations:
class Program {
delegate IEnumerable<T> FilterDelegate<T>(IEnumerable<T> collection) where T : class, INumericValue;
delegate T SelectDelegate<T>(IEnumerable<T> collection, ref T previous) where T : class, INumericValue;
delegate T CombinedDelegate<T>(IEnumerable<T> collection, ref T previous) where T : class, INumericValue;
static void Main() {
Test previous = new Test(6);
List<Test> collection = new List<Test>();
FilterDelegate<Test> filter = Filter;
SelectDelegate<Test> select = Select;
for (int i = 0; i < 10; i++)
collection.Add(new Test(i));
// use explicit types to be able to use ref in lambda
CombinedDelegate<Test> combined = (IEnumerable<Test> c, ref Test p) => @select(filter(c), ref p);
Test result = combined(collection, ref previous);
//Expected result Test with Number = 7
}
static IEnumerable<T> Filter<T>(IEnumerable<T> collection) where T : class, INumericValue {
return collection.Where(c => c.Number > 3);
}
static T Select<T>(IEnumerable<T> collection, ref T previous) where T : class, INumericValue {
// this didn't compile so I changed it.
foreach (var item in collection) {
if (item.Number > previous.Number) {
previous = item;
break;
}
}
return previous;
}
}
You can use ref
and out
parameters with anonymous methods:
static GetItemDelegate<T> CreateDelegate<T>(FilterDelegate<T> filter, SelectorDelegate<T> select)
{
return delegate (IEnumerable<T> collection, ref T previous) {
return select(filter(collection), ref previous);
};
}
This is almost a comment to Lee's answer as it is just his answer with old-style C# 2.0 "anonymous methods" converted to C# 3.0 lambda syntax:
static GetItemDelegate<T> CreateDelegate<T>(FilterDelegate<T> filter, SelectorDelegate<T> select)
{
return
(IEnumerable<T> collection, ref T previous) => select(filter(collection), ref previous);
}
We note that C# 3.0 was released back in 2007 (Visual Studio 2008).
Remark: The correct declarations of your delegate types are:
delegate IEnumerable<T> FilterDelegate<T>(IEnumerable<T> collection);
delegate T SelectorDelegate<T>(IEnumerable<T> collection, ref T previous);
//Combine above two to this one:
delegate T GetItemDelegate<T>(IEnumerable<T> collection, ref T previous);
The first one is entirely similar to Func<IEnumerable<T>, IEnumerable<T>>
of course.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With