"Joanna Carter [TeamB]" <jo****@not.for.spam> wrote:
"Barry Kelly" <ba***********@gmail.com> a écrit dans le message de news:
u7********************************@4ax.com...
| 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
generic Invoke<> method, while the thing I've appended takes in a
"params object[]".
| 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[i], _types[i]))
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
--
http://barrkel.blogspot.com/