Generics and Casting - strange behaviour

A

Andrew Ducker

I have a class "Validator" which can be cast to a Control. The code:
ValidTextBox t = (ValidTextBox)v;
works just fine.

However, because v doesn't descend from t, I can't use "is" or "as". I
did want a safe way of casting though - so I tried the following code:

public static T ConvertClass<T>(Validator myValidator) where T :
Control
{
T convertedObject;
try
{
convertedObject = (T)myValidator;
return convertedObject;
}
catch
{
return default(T);
}
}
and that worked fine. So I thought I'd make it more generic - no
reason this couldn't work for any two classes, after all.

public static T ConvertClass<T>(Object myObject)
{
T convertedObject;
try
{
convertedObject = (T)myObject;
return convertedObject;
}
catch
{
return default(T);
}
}

But that doesn't work. I get an exception thrown when it tries to
perform the cast, with the message:
Unable to cast object of type 'Validator' to type 'ValidTextBox'.
Which is clearly complete nonsense, as it will perform that cast if I
tell it to.

Anyone out there got an explanation?

Andy
 
J

Jon Skeet [C# MVP]

Andrew said:
I have a class "Validator" which can be cast to a Control. The code:
ValidTextBox t = (ValidTextBox)v;
works just fine.

However, because v doesn't descend from t, I can't use "is" or "as".

It sounds like there's a user-defined conversion from Validator to
ValidTextBox, which isn't used by is/as.

But that doesn't work. I get an exception thrown when it tries to
perform the cast, with the message:
Unable to cast object of type 'Validator' to type 'ValidTextBox'.
Which is clearly complete nonsense, as it will perform that cast if I
tell it to.

Well, it's not really nonsense if there's a user-defined conversion
going on. The compiler is able to pick that up in the non-generic case,
and it doesn't actually create a cast as far as the IL is concerned -
it executes the conversion. Generics doesn't use explicit user-defined
conversion, and in CLR terms it *can't* cast from Validator to
ValidTextBox. The fact that a cast-expression in C# works doesn't mean
there's a valid CLR cast.

Jon
 
A

Andrew Ducker

Jon said:
Well, it's not really nonsense if there's a user-defined conversion
going on. The compiler is able to pick that up in the non-generic case,
and it doesn't actually create a cast as far as the IL is concerned -
it executes the conversion.

Aaah, so t = (ValidTextBox)v; can get translated into two different
sets of IL at compile time, depending on whether the conversion is
user-defined or not, and as it can't tell at compile time which one
it's going to need it assumes the straightforward cast is going to be
ok, which then throws an exception when it's not.

Is there a way for me to code around this - to try a straightforward
cast, then try a conversion, and if both fail then return null?

Andy
 
J

Jon Skeet [C# MVP]

Andrew said:
Aaah, so t = (ValidTextBox)v; can get translated into two different
sets of IL at compile time, depending on whether the conversion is
user-defined or not, and as it can't tell at compile time which one
it's going to need it assumes the straightforward cast is going to be
ok, which then throws an exception when it's not.
Yup.

Is there a way for me to code around this - to try a straightforward
cast, then try a conversion, and if both fail then return null?

The best thing to do, if you have access to Validator, is to have some
sort of generic conversion interface so it doesn't need to rely on
compile cleverness in the first place. I don't believe there's any way
of using user-defined conversions in a generic context.

Jon
 
A

Andrew Ducker

Jon said:
The best thing to do, if you have access to Validator, is to have some
sort of generic conversion interface so it doesn't need to rely on
compile cleverness in the first place. I don't believe there's any way
of using user-defined conversions in a generic context.

Well, it can clearly manage, when the constraints are right - otherwise
the first coding example wouldn't work. I'll have to take a further
look into it when I have more time.

Cheers for the advice,

Andy
 
J

Jon Skeet [C# MVP]

Andrew said:
Well, it can clearly manage, when the constraints are right - otherwise
the first coding example wouldn't work. I'll have to take a further
look into it when I have more time.

Ah - I've just looked at the samples again. The second sample wouldn't
have worked even without generics. User-defined conversions are applied
at compile-time, so if you did:

Object o = myValidator;
ValidTextBox vtb = (ValidTextBox) o;

you'd run into the same exception.

I'm frankly surprised that the user-defined conversion is applied in a
generic way to start with though. I might have to play around with
that. I tend to avoid user-defined conversions, to be honest... I like
casts to really be casts :)

Jon
 
A

Andrew Ducker

Jon said:
I'm frankly surprised that the user-defined conversion is applied in a
generic way to start with though. I might have to play around with
that. I tend to avoid user-defined conversions, to be honest... I like
casts to really be casts :)

Oh, me too, generally. I was trying to find a way around the lack of
multiple inheritance by having each of my subclasses of the subclasses
of Winforms.Control contain a private Validator object, and being
castable to it. That way it _looked_ like it was multiple inheritance
without actually being multiple inheritance.

It started off as a way of avoiding having to implement IValidator in
all of the Controls subclasses (I hate having multiple copies of code)
and kind of spread from there...

Doesn't quite work though, as you can see.

Andy D
 

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