Recently I've run into strange issue related to casting. Every discussion/post I've seen tends to revolve around using casting when one is sure about the object being casted plus a couple of details. I haven't however found what's the reasoning behind the code below:
class Program
{
static void Main(string[] args)
{
var h = new SomeCommandHandler();
var c = h as ICommandHandler<ICommand>; //this works as expected
//var c = (ICommandHandler<ICommand>)h; //this throws - why?
}
interface ICommand { }
class SomeCommand : ICommand { }
interface ICommandHandler<I> where I : ICommand { }
class SomeCommandHandler : ICommandHandler<SomeCommand> { }
}
So why the second call throws an exception? What's the difference between casting and as operator that I'm not aware of?
EDIT:
It wpuld throw in the commented line above
"Unhandled Exception: System.InvalidCastException: Unable to cast object of type 'SomeCommandHandler' to type 'ICommandHandler`1[ConsoleApplication1.Program+ICommand]'"
Well, that's the whole entire difference right there. The as operator returns null if the object can't be cast to that type, and just casting produces an exception.
Others have already explained the difference between the direct cast throwing an exception and as returning null when the cast fails. In order to be able to make such a cast succeed you will need to make the generic interface contravariant:
interface ICommandHandler<out I> where I : ICommand { }
However, this might not be possible, depending on how the interface really looks (I am assuming that you are showing a stripped down version for brevity). If your interface contains method that accepts an argument of the type I this will not work; the type I must appear only in get-operations:
interface ICommandHandler<out I> where I : ICommand
{
void SetCommand(I n); // this would not be allowed...
I GetCommand(); // ...but this would.
}
It throws an exception because h is of type SomeCommandHandler which is ICommandHandler<SomeCommand> and you try to cast it to ICommandHandler<ICommand> whis is a different type.
The cast fails because an ICommandHandler<SomeCommand> is not an ICommandHandler<ICommand>. See here for more details & examples.
The as simply returns null when the instance is not of the specified type, whereas the cast throws an InvalidCastexception
the variable c is a is null. It doesn't throw because that's what using "as" means. In other words the instance h is Not an instance of ICommandHandler.
The next line throws because you're attempting to force a cast of an instance of SomCommandHandler to an instance of ICommandHandler
Make sense?
Related
I have the following generic static class which is being used in a Fluent API. It takes an input parameter and returns a wrapper class containing the parameter cast to the generic type.:
public static Foo<TOut> InputAs<TOut>(object parameter) {
var castParameter = parameter as TOut;
if(castParameter == null) {
throw new Exception("Invalid cast");
}
return new Foo<TOut>(castParameter);
}
The problem is that the castParameter == null check always returns null. What would be the correct way to cast the object using the TOut generic parameter as the new type?
Well, if parameter as TOut returns null, then the runtime type of parameter isn't TOut.
Don't forget that operator resolution is done at compile-time, so if you have cast operators defined, they will not be invoked here. If you do need that, you can use dynamic:
public static Foo<TOut> InputAs<TOut>(dynamic parameter)
{
return new Foo<TOut>((TOut)parameter);
}
This will allow runtime operator resolution, and will call your cast operator if one is available. For example, it will allow you to pass long, while expecting int.
However, you probably want to find a different way of what you're trying to do; dynamic can be very useful, but it can also make debugging quite a bit harder, and you lose almost all compile-time warnings and errors that can help you identify problems before they happen.
In a call back like this:
this.Model.LoadStudent(student_key, (o, a) =>
{
var student = o as Students;
If I put a break point on first line, debugger shows me that "o" has what I am looking for and also shows me its type which is "Students" but as soon as it goes to next line and does the cast, the result is null. Why? What is happening?
You're not casting - you're using the as operator. If you instead actually cast, I suspect you'll get an exception:
var student = (Students) o;
I suspect you've actually got multiple types called Students, and if you really cast instead of using as you'll see the actual type involved.
For example:
using System;
namespace Bad
{
class Students {}
class Test
{
static void Main()
{
object o = new Good.Students();
Students cast = (Students) o;
}
}
}
namespace Good
{
class Students {}
}
This leads to:
Unhandled Exception: System.InvalidCastException:
Unable to cast object of type 'Good.Students' to type 'Bad.Students'.
at Bad.Test.Main()
Note how you get the fully-qualified type names in the exception.
In general, it's a bad idea to use as unless you really expect that the object may not be of the right type, and usually you check for that afterwards. See my blog post on the topic for more details.
In that case your object isn't of the Students type. Casting in this way won't throw an error if you are casting to the wrong type it will just make the object null.
I'm writeing a generice method in C#:
private T GetMamConfigurations<T>(IDictionary<string, object> items,
MaMDBEntities maMDBEntities) where T : class
{
T geoConfigs = default(T);
if (typeStr.Equals("MamConfiguration", StringComparison.OrdinalIgnoreCase))
{
geoConfigs = (T)GetGeoConfigurationNumericFromDB(items, maMDBEntities);
}
else if (typeStr.Equals("ListOfMamConfiguration", StringComparison.OrdinalIgnoreCase))
{
geoConfigs = (T)GetGeoConfigurationsPercentageFromDB(items, maMDBEntities);
}
return geoConfigs;
}
GetGeoConfigurationNumericFromDB returns MamConfiguration
whereas
GetGeoConfigurationsPercentageFromDB returns IList<MamConfiguration>
and I get compliation error:
cannot cast expression of type MamConfiguration to type T
Why is that?
Is there any way to solve without forcinf the two methods to return IList<MamConfiguration> ?
Well, you can always placate the compiler by adding an object cast in the middle:
geoConfigs = (T)(object)GetGeoConfigurationNumericFromDB(items, maMDBEntities);
which will defer the type-check until runtime. However! There is no compiler way to do this otherwise, as it is never going to be happy with string tests like "MamConfiguration". Also, generics work well when the code is ... generic - i.e. does the same thing which each type. The code shown is the opposite of generic. It is non-generic code exposed through a generic API. It is always going to be messy inside. Personally I would try to avoid this usage in the first place.
Instead of using (T) to cast to T, you can use the as operator. as will return null if the conversion fails. If it succeeds, it will return your converted value.
geoConfigs = GetGeoConfigurationNumericFromDB(items, maMDBEntities) as T;
Is it possible to use Generic mechanism within function body?
for exammple
if (!(someClass is IClass<T, G> where T : someInterface, G : anotherInterface))
{
return;
}
or do casting like this:
var v = (IClass <T, G> where T : someInterface, G: anotherInterface)something;
You can do this, but you have to make sure your interface is covariant:
interface IClass<out T, out S>
{
// Methods that can return a T or S but not accept one as input
}
By marking the type parameters as out, you are basically saying "I will only ever get a T or an S out of this interface". For example, IEnumerable<out T> as you can only get a T out of it, but only List<T> because you can put a T into a list as well as get one out.
Having defined your interface as such, an IClass<string, string> is an IClass<object, object>: you know your IClass<string, string> will only ever give you a string, but since a string is an object then that's fine, and if you assign it to an IClass<object, object> you know it will only ever give you an object.
(You can't do this if you interface allows you to put a T or an S into something. If this was the case, and you assigned your IClass<string, string> to an IClass<object, object>, you could try to put an int into it and it would fail, because the underlying class only really accepts a string.)
What this then lets you do is
if (!(something is IClass<object, object>))
{
return;
}
or
var v = (IClass<object, object>)something;
and both will work if something is actually an object that implements, say, IClass<string, string>.
For the first example, yes - you just need to specify the types:
if (!(someClass is IClass<ISomeInterface, IAnotherInterface>))
{
return;
}
The other is muct the same:
var v = (IClass <ISomeInterface, IAnotherInterface>)something;
Although its probably better to use as
var v = something as IClass <ISomeInterface, IAnotherInterface>;
if(v != null)
{
// Do something.
}
The second line above is important - by using as in place of a direct cast you wont get an InvalidCastException if it fails, but v will be null if the cast using as fails. This technique gives you a little more control over failure if your attempt to cast is invalid. (Consider why the conversion would be invalid: if that represents a situation where you know longer know what the world looks like, the InvalidCastException is probably the correct approach. If it's reasonable for the object not to be an instance of the interface, then as is your friend.)
you could try this:
if (!(someClass is IClass<someInterface, anotherInterface>)
{
return;
}
or via reflection:
var t = someClass.GetType()
Type[] typeParameters = t.GetGenericArguments();
if (!typeParameters[0].IsSubclassOf(typeof(someInterface)) ||
!typeParameters[1].IsSubclassOf(typeof(anotherInterface)))
{
return;
}
where constraints are to be applied to the generic method itself. You can use the desired types with no problem.
On a side note: don't check for false, it's more confusing on the long run.
if (someClass is IClass<someInterface, anotherInterface>)
{
// your code
}
// else { return; } // no longer needed!
Expanding and enveloping the code in a generic method, it would look like this:
void myMethod<T, U>()
where T : someInterface
where U : anotherInterface
{
if (someClass is IClass<T, U>)
{
// your code
}
// else { return; } // no longer needed!
}
I have 2 classes:
class A {
public void A_1(Object b) {
...
Type t = b.GetType();
(t.FullName)b.B_1(); //It doesn`t work! Error in cast
}
....
}
class B {
public void B_1() {
...
}
....
}
A a = new A();
B b = new B();
a.A1(b);
How to cast object correctly?
If you want to cast an object of any type to an object of another type, you do this:
// Will Throw an exception at runtime if it cant be cast.
B newObject = (B)oldObject;
// Will return null at runtime if the object cannot be cast
B newObject = oldObject as B;
// If in a generic method, convert based on the generic type parameter regardless of condition - will throw an exception at runtime if it cant be cast
B newObject = (T)Convert.ChangeType(oldObject, typeof(T))
Your syntax is off; you don't convert from the fullname to the object, you simply convert from the type symbol.
double x = (double)40;
ClassB anotherInstance = (ClassB)someOtherInstance;
What you're trying to do is basically:
Foo myFoo = ("Foo")myObject;
That definitely will not work in C#. When you cast in C#, the compiler emits code that does the cast, it needs to know what it's casting from and to, in order to write that code. A string does not help the compiler out here.
As others have pointed out, what you want to do doesn't seem like you really need to (unless this is just a contrived example). If you really want to do this, you'll need to work with a more dynamic language than C#, or find a C# friendly way of accomplishing this.
Are you sure you didn't mean to do (B)b.B_1()?
C# has a static type-system, i.e. all types must be known at compile-time (modulo reflection). So, casting to a type that is only known at run-time makes no sense. Specify the type explicitly:
public void A_1(object obj)
{
...
B b = (B)obj;
b.B_1();
// or
((B)obj).B_1();
}
You can also do this:
class A {
public void A_1(Object b) {
...
if (b is B)
{
((B)b).B_1();
}
}
....
}
Type.FullName is just a string; it's not a type. Use this instead: ((B)b).B_1(); Also, using GetType() is a way to get the type of an object dynamically, but casting is only possible or useful when the target type is known at compile time (not dynamic at all). In order to cast, simply refer to the type directly in a pair of parentheses. Don't attempt to obtain or use an object of type Type.