Generic Interfaces and casting from object at runtime problem

A

Anthony Paul

Let's say that I would like a generic type that supports Min/Max
properties and can be double or integer or even datetime if need be,
something flexible.

So I go about creating the following generic interface :

*note : in reality I implement the IComparable and IEquatable generic
interfaces and associated overriden methods, but I've cut everything
down to the bare minimum for this example.

public interface IMinMax<T>
{
T Min{get;}
T Max{get;}
}

and the following generic struct :

public struct MinMax<T> : IMinMax<T>
{
private readonly T min;
private readonly T max;

public T Min
{
get
{
return min;
}
}

public T Max
{
get
{
return max;
}
}

public MinMax(T min, T max)
{
this.min = min;
this.max = max;
}

}


Now here's some code to use it :

IMinMax<int> intMinMax = new MinMax<int>(0, 100); // percentage range
IMinMax<d> dateMinMax = new MinMax<DateTime>(new DateTime(1973, 10,
4), DateTime.Now); // date range


Okay, so here's the problem... what if I have the following
procedure :

public void DoSomething(object o)
{
IMinMax<> mm = (IMinMax<>) o; // this doesn't work

// do something with mm.Min and mm.Max here
}

and I want to call the procedure as follows :

DoSomething(intMinMax);
DoSomething(dateMinMax);


How do we go about doing something with Min and Max? Obviously there's
a lot of meat missing in the code and I simplified it quite
unrealistically for the purpose of this newsgroup so please no
questions as to why I would want to do it... this comes up all the
time in one form or another.

I guess the real question is... once you've cast a generic interface
to an object, how do you go about extracting its information at run-
time? In my case I happen to know the type at runtime but the
following modified method still doesn't work :

public void DoSomething(Type t, object o)
{
IMinMax<t> mm = (IMinMax<t>) o; // still doesn't work

// do something with mm.Min and mm.Max here
}


Regards!

Anthony
 
S

Sheng Jiang[MVP]

Your struct (or value type) is boxed and the type information seems lost
when it is converted to object..
You can try C++/CLI, which retains type information when you box a value
type.
Reference
C++: The Most Powerful Language for .NET Framework Programming by Kenny
Kerr, MSDN
 
J

Jon Skeet [C# MVP]

Sheng Jiang said:
Your struct (or value type) is boxed and the type information seems lost
when it is converted to object..

No, type information is certainly *not* lost when it's boxed. Just try
unboxing something to the wrong type - you'll find out soon enough.
 
S

Sheng Jiang[MVP]

It seems he did not specify the type for the generics
this program runs fine
static void Main(string[] args)
{
IMinMax<int> intMinMax = new MinMax<int>(0, 100); // percentage
range
IMinMax<DateTime> dateMinMax = new MinMax<DateTime>(new
DateTime(1973, 10,
4), DateTime.Now); // date range
DoSomething<int>(intMinMax);
DoSomething<DateTime>(dateMinMax);

}
public static void DoSomething<T>(object o)
{
IMinMax<T> mm = (IMinMax<T>) o;

// do something with mm.Min and mm.Max here
}
 
J

Jon Skeet [C# MVP]

Sheng Jiang said:
It seems he did not specify the type for the generics
this program runs fine

That still doesn't mean that boxing loses type information as you
claimed.
static void Main(string[] args)
{
IMinMax<int> intMinMax = new MinMax<int>(0, 100); // percentage
range
IMinMax<DateTime> dateMinMax = new MinMax<DateTime>(new
DateTime(1973, 10,
4), DateTime.Now); // date range
DoSomething<int>(intMinMax);
DoSomething<DateTime>(dateMinMax);

}
public static void DoSomething<T>(object o)
{
IMinMax<T> mm = (IMinMax<T>) o;

// do something with mm.Min and mm.Max here
}

Yes, but if he changed DoSomething<T> to accept IMinMax<T> directly,
there'd be no need for the cast, he'd gain more compile-time type
safety, and he wouldn't have to specify the type parameter at the call
site.
 
S

Sheng Jiang[MVP]

in Managed C++, you do not have to lose static type information when you box
a value. This is something that C# does not provide.

--
Sheng Jiang
Microsoft MVP in VC++
Jon Skeet said:
Sheng Jiang said:
It seems he did not specify the type for the generics
this program runs fine

That still doesn't mean that boxing loses type information as you
claimed.
static void Main(string[] args)
{
IMinMax<int> intMinMax = new MinMax<int>(0, 100); // percentage
range
IMinMax<DateTime> dateMinMax = new MinMax<DateTime>(new
DateTime(1973, 10,
4), DateTime.Now); // date range
DoSomething<int>(intMinMax);
DoSomething<DateTime>(dateMinMax);

}
public static void DoSomething<T>(object o)
{
IMinMax<T> mm = (IMinMax<T>) o;

// do something with mm.Min and mm.Max here
}

Yes, but if he changed DoSomething<T> to accept IMinMax<T> directly,
there'd be no need for the cast, he'd gain more compile-time type
safety, and he wouldn't have to specify the type parameter at the call
site.
 
S

Sheng Jiang[MVP]

http://msdn2.microsoft.com/en-us/library/ms379617(VS.80).aspx#vs05cplus_topic7
not related to this case though
--
Sheng Jiang
Microsoft MVP in VC++
Jon Skeet said:
Sheng Jiang said:
It seems he did not specify the type for the generics
this program runs fine

That still doesn't mean that boxing loses type information as you
claimed.
static void Main(string[] args)
{
IMinMax<int> intMinMax = new MinMax<int>(0, 100); // percentage
range
IMinMax<DateTime> dateMinMax = new MinMax<DateTime>(new
DateTime(1973, 10,
4), DateTime.Now); // date range
DoSomething<int>(intMinMax);
DoSomething<DateTime>(dateMinMax);

}
public static void DoSomething<T>(object o)
{
IMinMax<T> mm = (IMinMax<T>) o;

// do something with mm.Min and mm.Max here
}

Yes, but if he changed DoSomething<T> to accept IMinMax<T> directly,
there'd be no need for the cast, he'd gain more compile-time type
safety, and he wouldn't have to specify the type parameter at the call
site.
 
J

Jon Skeet [C# MVP]

Sheng Jiang said:
in Managed C++, you do not have to lose static type information when you box
a value. This is something that C# does not provide.

Ah, you mean that C++ exposes the boxed type separately from the
unboxed type? Nice.
 
A

Anthony Paul

Hello Sheng!

Thanks for replying...

The problem with the solution you suggested is that I would have to
modify the DoSomething procedure to accept a generic type. My question
was (and given that *exact* scenario I described) how would I go about
using a generic object after it has been cast to an object and passed
as a parameter via a method that knows nothing about the generic type?
I was even generous enough to allow for the possibility of me already
knowing the generic type, but not passed in as a generic type to the
method but through a parameter of type Type as in the following :

public void DoSomething(object o, Type t)

This, of course, is a horrible example but given this exact scenario,
how would I be able to cast o back to its original form and use it?
And how would be go about doing the same if we didn't happen to have
the luxury of knowing the generic type as in the following :

public void DoSomething(object o)
{
// how do I cast o to the appropriate IMinMax<T> and use it here?
}

Regards,

Anthony

It seems he did not specify the type for the generics
this program runs fine
static void Main(string[] args)
{
IMinMax<int> intMinMax = new MinMax<int>(0, 100); // percentage
range
IMinMax<DateTime> dateMinMax = new MinMax<DateTime>(new
DateTime(1973, 10,
4), DateTime.Now); // date range
DoSomething<int>(intMinMax);
DoSomething<DateTime>(dateMinMax);

}
public static void DoSomething<T>(object o)
{
IMinMax<T> mm = (IMinMax<T>) o;

// do something with mm.Min and mm.Max here
}

--
Sheng Jiang
Microsoft MVP in VC++


No, type information is certainly *not* lost when it's boxed. Just try
unboxing something to the wrong type - you'll find out soon enough.

- Show quoted text -
 
A

Anthony Paul

Hello Jon,

In reply to your post on the other duplicate thread (I had no idea it
was posted twice) as to why I didn't pass the generic type via the
generic method call :

public void DoSomething<T>(object o)


I can't do this because it mean making an assumption that object "o"
is of a certain generic type when that may not be the case. What if
it's just a regular old int? or a string? It could be an IMinMax<T> in
which case the T would come in handy, but what if it was a... let's
say, ISeries<T, U, V> ??

Regards,

Anthony
 
J

Jon Skeet [C# MVP]

Anthony Paul said:
In reply to your post on the other duplicate thread (I had no idea it
was posted twice) as to why I didn't pass the generic type via the
generic method call :

public void DoSomething<T>(object o)

I can't do this because it mean making an assumption that object "o"
is of a certain generic type when that may not be the case. What if
it's just a regular old int? or a string? It could be an IMinMax<T> in
which case the T would come in handy, but what if it was a... let's
say, ISeries<T, U, V> ??

Then your cast on the first line of DoSomething is going to throw an
exception immediately.
 
A

Anthony Paul

Yes... that's my point, it won't work. So my question is... how does
one go about doing it?
 
J

Jon Skeet [C# MVP]

Anthony Paul said:
Yes... that's my point, it won't work. So my question is... how does
one go about doing it?

Doing what, exactly? You can't pretend that something implements an
interface if it doesn't.

I originally thought that your problem was not knowing what kind of
IMinMax<T> to cast to (i.e. what to use as T). If you're wanting to use
something which *isn't* an IMinMax<T> as if it is, that's a completely
different question.
 
S

Sergey Zhukov

I am looking for solution of this problem too. At this time, I use
reflection to determine the generic type of the interface and then
Invoke interface methods. But this approach generates not easy readable
code...
 
J

Jon Skeet [C# MVP]

I am looking for solution of this problem too. At this time, I use
reflection to determine the generic type of the interface and then
Invoke interface methods. But this approach generates not easy readable
code...

When you're using reflection, the result is almost never readable
code. Generics do make it worse though - it's an extra dimension to
think about, effectively :(

Jon
 

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