Generics with List and Delegates?

G

Guest

I wasn't sure if I could do this. I believe I am stretching the capability of
what generics can do for me but here goes.

I have a generic delegate defined as
public delegate bool RuleDelegate<T>(T item);

In my class my goal is to use a generic list collection to contain my
generic delegates. This will allow me to pass this List to another library
and call this list of functions. This could provide a new way to build rule
base engines in .NET if this is possible thus being independant of
implementation. Is this possible with .NET 2.0?
 
J

Joanna Carter [TeamB]

"Cedric Rogers" <[email protected]> a écrit dans le message de
news: (e-mail address removed)...

|I wasn't sure if I could do this. I believe I am stretching the capability
of
| what generics can do for me but here goes.
|
| I have a generic delegate defined as
| public delegate bool RuleDelegate<T>(T item);
|
| In my class my goal is to use a generic list collection to contain my
| generic delegates. This will allow me to pass this List to another
library
| and call this list of functions. This could provide a new way to build
rule
| base engines in .NET if this is possible thus being independant of
| implementation. Is this possible with .NET 2.0?

As far as I have been able to ascertain, it is not possible to have a list
of generic delegates, they would have to be bound to one particular type.

However, I would be grateful to find that I am wrong, because it is
something that I had hoped to do.

Joanna
 
B

Barry Kelly

Cedric Rogers said:
I wasn't sure if I could do this. I believe I am stretching the capability of
what generics can do for me but here goes.

I have a generic delegate defined as
public delegate bool RuleDelegate<T>(T item);

In my class my goal is to use a generic list collection to contain my
generic delegates. This will allow me to pass this List to another library
and call this list of functions. This could provide a new way to build rule
base engines in .NET if this is possible thus being independant of
implementation. Is this possible with .NET 2.0?

I'm not sure exactly what you're asking for. Why isn't:

List<RuleDelegate<int>> intRuleList = // ...

good enough?

Is it that you want all the delegates in the list to have the same
signature? In that case, why isn't:

delegate object DynamicInvokeMethod(params object[] args);
List<DynamicInvokeMethod> rules = new List<DynamicInvokeMethod>();
rules.Add(myIntDelegate.DynamicInvoke);
rules.Add(myFooDelegate.DynamicInvoke);

good enough?

Is it that you want pattern matching on the basis of the type of the
argument? In that case, why not write a class which uses the above
DynamicInvoke along with perhaps a dictionary mapping the type of the
argument to the corresponding delegates?

Generics are a tool for static typing - they may do dynamic work at
*compile* time, but everything must be statically resolved before the
compilation ends. If you're trying to get dynamic typing behaviour,
generics aren't a sufficient tool. However, with the ability in .net to
reflect over the generic parameters of any particular instantiation,
they can still be useful in dynamic situations.

-- Barry
 
J

Joanna Carter [TeamB]

"Barry Kelly" <[email protected]> a écrit dans le message de (e-mail address removed)...

| I'm not sure exactly what you're asking for. Why isn't:
|
| List<RuleDelegate<int>> intRuleList = // ...
|
| good enough?

Because I think Cedric wants a list of RuleDelegate<T> (unbound).

| Is it that you want all the delegates in the list to have the same
| signature? In that case, why isn't:
|
| delegate object DynamicInvokeMethod(params object[] args);
| List<DynamicInvokeMethod> rules = new List<DynamicInvokeMethod>();
| rules.Add(myIntDelegate.DynamicInvoke);
| rules.Add(myFooDelegate.DynamicInvoke);
|
| good enough?

I can't quite work out what you are doing here, this also seems to need a
bound delegate type to work. I do understand that generics are not meant to
allow dynamlic typing, but I also have come against a problem when I need to
use a non-generic method to link a generic event to a generic handler.

| Is it that you want pattern matching on the basis of the type of the
| argument? In that case, why not write a class which uses the above
| DynamicInvoke along with perhaps a dictionary mapping the type of the
| argument to the corresponding delegates?

Hmmm, this looks interesting, I will have to investigate this. Have you
actually used this technique, or is it pure untested inspiration ? :)

| Generics are a tool for static typing - they may do dynamic work at
| *compile* time, but everything must be statically resolved before the
| compilation ends. If you're trying to get dynamic typing behaviour,
| generics aren't a sufficient tool. However, with the ability in .net to
| reflect over the generic parameters of any particular instantiation,
| they can still be useful in dynamic situations.

I feel that generics were introduced, in part, to resolve the speed penalty
of having to use reflection, so I am loathe to have to use reflection to
resolve generics issues.

If I want a list of a generic *type*, then I will create a non-generic base
class and derive the generic class from it. This then allows me to hold a
list of the non-generic base type, regardless of the bound parameter.

However, I *think* that there are occasions when I want to have a list of
unbouond delegates and would want to use the same "base non-generic
delegate" technique, which is not possible.

Could I trouble you to elucidate further on your DynamicInvokeMethod idea ?

Joanna
 
B

Barry Kelly

Joanna Carter said:
"Barry Kelly" <[email protected]> a écrit dans le message de (e-mail address removed)...

| I'm not sure exactly what you're asking for. Why isn't:
|
| List<RuleDelegate<int>> intRuleList = // ...
|
| good enough?

Because I think Cedric wants a list of RuleDelegate<T> (unbound).

How would you use such a type? You couldn't call it, because you can't
provide values of type 'T' (the Invoke method on the delegate isn't
itself generic). You couldn't add anything to it, because it's
impossible to create object instances of type 'RuleDelegate<T>' (the
method table entry for the Invoke method would need to point to a method
that manipulates a fictional type - T).

Can you write the code that adds to the list, and calls items in the
list? From what I can see, you might be after pattern matching in the
functional style, as seen in languages like Haskell.

The problem is, if you've got two delegates in there, one taking int and
the other taking string, and now you want to call the delegates and
you've got (say) a string value, what bits succeed due to static type
checking? What bits fail?
| Is it that you want all the delegates in the list to have the same
| signature? In that case, why isn't:
|
| delegate object DynamicInvokeMethod(params object[] args);
| List<DynamicInvokeMethod> rules = new List<DynamicInvokeMethod>();
| rules.Add(myIntDelegate.DynamicInvoke);
| rules.Add(myFooDelegate.DynamicInvoke);
|
| good enough?

I can't quite work out what you are doing here, this also seems to need a
bound delegate type to work.

What could you possibly do with an instance of an open generic type? Can
you write some (pseudo)code showing how to use an instance of an open
generic type?
I do understand that generics are not meant to
allow dynamlic typing, but I also have come against a problem when I need to
use a non-generic method to link a generic event to a generic handler.

Can you say more about what a "generic event" is? Or do you mean a
generic delegate, say something like:

void Foo<T>(T value) { Console.WriteLine(value); }

// ...

Action<T> action = Foo;

.... such that whenever you call action(x), it instantiates the correct
Foo<typeof(x)> and invokes it?

This is effectively trying to imperatively create an alias for an
existing generic method. For this to work, the Invoke() method on
Action<T> would need to be generic itself, and when it is instantiated,
it would need to instantiate the right Foo<T> and invoke it.

A problem with that is that the assignment or delegate combine is
imperative, so for any given Action<T>, the compiler couldn't reliably
know what Foo<T> (and/or Bar<T> etc.) it is attached to, due to the
halting problem. Thus, when generating code for our mythical
Action<T>.Invoke<T>(T value) (Invoke would need to become generic), it
couldn't produce code which actually instantiates a Foo<T> or a Bar<T>.
Instead, it would need to use reflection to look up the open generic
methods it is bound to, and fill in the generic arguments for each one
of them, and finally invoke each of them.

A big point of generics is that it can lead to performance improvements
due to elimination of reflection or other dynamic typing techniques. So,
in order to avoid doing that work each time, it would need to cache the
values somewhere. It can't be directly in fields, because that would
cause the physical object size to grow dynamically. Thus it would have
to store a dictionary mapping the types of the arguments to the
instances of Foo<>/Bar<> etc.

I think at this point the compiler is producing a bunch of code that
isn't a whole lot better than what you or I could write ourselves using
Reflection and Reflection.Emit.

To be clear, this thing I'm talking about here is something different
from the thing I've appended to the document. The thing here needs a
| Is it that you want pattern matching on the basis of the type of the
| argument? In that case, why not write a class which uses the above
| DynamicInvoke along with perhaps a dictionary mapping the type of the
| argument to the corresponding delegates?

Hmmm, this looks interesting, I will have to investigate this. Have you
actually used this technique, or is it pure untested inspiration ? :)

It was sourced from functional programming. I have used Reflection.Emit
to avoid reflection overhead when dynamically binding to a method, but
it was a slightly different scenario.
| Generics are a tool for static typing - they may do dynamic work at
| *compile* time, but everything must be statically resolved before the
| compilation ends. If you're trying to get dynamic typing behaviour,
| generics aren't a sufficient tool. However, with the ability in .net to
| reflect over the generic parameters of any particular instantiation,
| they can still be useful in dynamic situations.

I feel that generics were introduced, in part, to resolve the speed penalty
of having to use reflection, so I am loathe to have to use reflection to
resolve generics issues.

If I want a list of a generic *type*, then I will create a non-generic base
class and derive the generic class from it. This then allows me to hold a
list of the non-generic base type, regardless of the bound parameter.

However, I *think* that there are occasions when I want to have a list of
unbouond delegates and would want to use the same "base non-generic
delegate" technique, which is not possible.

Could I trouble you to elucidate further on your DynamicInvokeMethod idea ?

The pattern-matching based on the types of the arguments is tricky,
because you've got to consider derived types etc. For example, if you
have two methods:

Foo(string, object)

and

Foo(object, string)

both added to the delegate, and you then try to call:

foo(null, null)

which one should it call? All possible ones? Exact matches?

The approach I've taken below is to call all possible matches. The code
isn't totally error-checked, YMMV. Hacked up in a few minutes, etc. :)

---8<---
using System;
using System.Collections.Generic;
using System.Reflection;

class DynamicDelegate
{
private List<DelegatePattern> _patterns =
new List<DelegatePattern>();

public DynamicDelegate()
{
}

public void Add(Delegate d)
{
if (d == null)
throw new ArgumentNullException("d");

int index = IndexOf(d.GetType());
DelegatePattern pattern;
if (index != -1)
pattern = _patterns[index];
else
{
pattern = new DelegatePattern(d.GetType());
_patterns.Add(pattern);
}

pattern.Target = Delegate.Combine(pattern.Target, d);
}

public void Remove(Delegate d)
{
if (d == null)
throw new ArgumentNullException("d");

int index = IndexOf(d.GetType());
if (index == -1)
return;

DelegatePattern pattern = _patterns[index];
pattern.Target = Delegate.Remove(pattern.Target, d);
if (pattern.Target == null)
_patterns.RemoveAt(index);
}

private int IndexOf(Type delegateType)
{
return _patterns.FindIndex(delegate(DelegatePattern item)
{
return object.Equals(item.DelegateType, delegateType);
});
}

public object Invoke(params object[] arguments)
{
// Convert "Invoke(null)" to "Invoke(new object[] { null })".
if (arguments == null)
arguments = new object[] { null };

object result = null;
foreach (DelegatePattern pattern in _patterns)
if (pattern.IsMatch(arguments))
result = pattern.Target.DynamicInvoke(arguments);
return result;
}

class DelegatePattern
{
private Type[] _types;
private Type _delegateType;
private Delegate _target;

public DelegatePattern(Type delegateType)
{
_types =
Array.ConvertAll<ParameterInfo,Type>(
delegateType.GetMethod("Invoke").GetParameters(),
delegate(ParameterInfo p)
{ return p.ParameterType; });
}

public bool IsMatch(object[] arguments)
{
if (arguments.Length != _types.Length)
return false;
for (int i = 0; i < arguments.Length; ++i)
if (!IsAssignableTo(arguments, _types))
return false;
return true;
}

private bool IsAssignableTo(object source, Type target)
{
if (source == null)
{
if (target.IsGenericType
&& target.GetGenericTypeDefinition() ==
typeof(Nullable<>))
return true;
return !target.IsValueType;
}
return target.IsInstanceOfType(source);
}

public Delegate Target
{
get { return _target; }
set { _target = value; }
}

public Type DelegateType
{
get { return _delegateType; }
set { _delegateType = value; }
}
}
}

class App
{
static void Main()
{
DynamicDelegate d = new DynamicDelegate();
d.Add(new Action<string>(Write));
d.Add((Action<int>) delegate(int x)
{ Console.WriteLine("An int: {0}", x); });
d.Add((Action<object>) delegate(object o)
{ Console.WriteLine("An object: {0}", o); });
d.Add(new Action<int?>(Write));

d.Invoke("some string");
d.Invoke(42);
d.Invoke(null);
}

static void Write<T>(T value)
{
Console.WriteLine("A '{0}': {1}", typeof(T).Name, value);
}
}
--->8---

-- Barry
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top