Problem using Reflection to Assign Generic Delegates

  • Thread starter Thread starter Joanna Carter \(TeamB\)
  • Start date Start date
J

Joanna Carter \(TeamB\)

Hi folks

I have a Generic Value Type and I want to detect when the internal value
changes.

///////////////////////////////
public delegate void ValueTypeValidationHandler<T>(T oldValue, T newValue);

public struct TestType<T>
{
private T value;

public event ValueTypeValidationHandler<T> valueChanged;

public ValueType(T value)
{
this.value = value;
valueChanged = null;
}

public T Value
{
get { return value; }
set
{
if(valueChanged != null)
valueChanged(this.value, value);

this.value = value;
}
}
}
//////////////////////

I want to be able to add TestType<> fields to a containing class and have
the containing class assign a delegate to each of the TestType<> fields
without naming them specifically, in the constructor of the containing
class.

So far I have got this far but I want to know how to attach the delegates to
the events.

/////////////////////////
public ContainingType()
{
FieldInfo[] fia = GetType().GetFields(BindingFlags.Instance |
BindingFlags.NonPublic);

foreach(FieldInfo fi in fia)
{
Type ft = fi.FieldType;

object fObj = fi.GetValue(this);

// the next line doesn't compile; how do I pass the type of each field into
the
generic parameters ?

((TestType<ft>) fObj).valueChanged += new
ValueTypeValidationHandler<ft>(HandleValueChanged<ft>);
}
////////////////////////////

TIA

Joanna
 
Hi!

How have you declared your HandleValueChanged method?

Thanks for replying...

private void HandleValueChanged<T>(T oldValue, T newValue) {...}

Have I made it clear enough what I am trying to do ? Is this something to do
with 'method groups' ?

Joanna
 
Joanna,
the containing class assign a delegate to each of the TestType<> fields
without naming them specifically

How important is this? It would sure make the code cleaner if you
could just name the fields rather than using Reflection.

If you start using Reflection you pretty much have to do it all the
way, and it gets kind of ugly. I think something like this shuld do it


Debug.Assert(ft.HasGenericArguments && ft.GetGenericArguments().Length
== 1 );
Type typeArgument = ft.GetGenericArguments()[0];

Type handlerType =
typeof(ValueTypeValidationHandler<object>).GetGenericTypeDefinition().BindGenericParameters(
new Type[] {typeArgument} );
Delegate handler = Delegate.CreateDelegate( handlerType,
typeof(ContainingType).GetMethod("HandleValueChanged",
BindingFlags.Instance|BindingFlags.NonPublic).BindGenericParameters(
typeArguments ) );
ft.GetEvent( "valueChanged" ).AddEventHandler( fObj, handler );


But this will only work as expected if you change TestType to a class,
because otherwise you just end up modifying a boxed copy of the field.



Mattias
 
How important is this? It would sure make the code cleaner if you
could just name the fields rather than using Reflection.

The idea is to have a method called from the base class constructor that
takes care of linking up a delegate in the containing class to the
ValueChanged events in my ValueType fields. Then all that has to be done in
derived classes is to add fields appropriate to the business class required.

In a previous version of this framework I wrote in Delphi, I simply added
the ValueTypes to a HashTable and accessed them by named index; I was just
experimenting with generics and trying to cut down on coding :-)

I could always revert to the standard Observer pattern, but I am trying to
exploit the built-in broadcasting mechanism already provided by multicast
delegates.
If you start using Reflection you pretty much have to do it all the
way, and it gets kind of ugly. I think something like this shuld do it
<snip>

I will try this but I think your next comment about boxing may well be the
problem.
But this will only work as expected if you change TestType to a class,
because otherwise you just end up modifying a boxed copy of the field.

Is there no way to unbox the TestType from the object? I thought casting it
to the original type would extract it, but that doesn't seem to work.

Regards

Joanna
 
Joanna,
The idea is to have a method called from the base class constructor that
takes care of linking up a delegate in the containing class to the
ValueChanged events in my ValueType fields.

Sounds like a potentially dangerous design. By the time the base class
constructor runs, the fields in the derived class may not have been
initialized yet.

Is there no way to unbox the TestType from the object?

Sure, but to unbox you need compile time knowledge of the type you
want to cast too, i.e. if it's a TestType<Foo> or a TestType<Bar>.
Which brings be back to the point that using Reflection here may just
be problematic.



Mattias
 
Sounds like a potentially dangerous design. By the time the base class
constructor runs, the fields in the derived class may not have been
initialized yet.

You are, of course, right; I am used to being able to call code before the
base constructor in Delphi.
Sure, but to unbox you need compile time knowledge of the type you
want to cast too, i.e. if it's a TestType<Foo> or a TestType<Bar>.

I had read about using unbound generic types and was hoping that that might
have provided an answer :-(
Which brings be back to the point that using Reflection here may just
be problematic.

OK, I'm still trying to adjust from Delphi's RTTI to reflection and finding
out what it can and cannot do.

Joanna
 
Back
Top