我有这样的课程:
// Some helper function in some manager
- (Transaction * _Nonnull)getTransactionFromCoreDataWithId:(NSString * _Nonnull)dataId
{
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:[Transaction entityName]];
request.returnsObjectsAsFaults = NO;
NSError *error;
NSArray<Transaction *> *fetchedObjects = [self.m_coreDataStore.managedObjectContext executeFetchRequest:request error:&error];
NSAssert(fetchedObjects != nil, @"Failed to execute %@: %@", request, error);
if(fetchedObjects == nil || fetchedObjects.count == 0)
{
// error
return nil;
}
for(Transaction *t in fetchedObjects)
{
Data *td = t.m_kmsTransactionData;
if([td.m_salesRecordId isEqualToString:salesRecordId])
{
return t;
}
}
return nil;
}
我不能这样做:
public class A
{
public int AProperty { get; set; }
public List<A> Children;
}
public class B:A
{
public string Name { get; set; }
}
我可以这样做:
A a = new A();
B b = (B)a;//SystemCastInvalidException
我错过了什么?为什么我可以在B bCanDo= new B();
bCanDo.Children.Add(new B());
foreach (var c in bCanDo.Children)
{
B notExpected = (B)c;//OKAY. Why?
}
投降?是的,这在逻辑上都是正确的,但我可以在哪里阅读有关它的信息?
答案 0 :(得分:4)
这是运行时错误,而不是编译器错误,所以让我们看看你在这里做了什么:
在第一个示例中,您构建的是A
类型的对象,然后尝试将其强制转换为B
类型。这是非法的,因为A
不会从B
继承。它是不非法,因为编译器认为这样,它编译代码,然后它在运行时崩溃,因为这肯定是无效的强制转换。
但是,在第二个示例中,您构建的是B
类型的对象,然后将其添加到可以容纳A
类型对象的列表中。由于B
继承自A
,因此这是合法的。然后,您选择退出第一个对象并将其强制转换为B
。这也是合法的,因为底层对象实际上是B
类型。
基本上,这是您的两个示例,其中包含更少的步骤:
A a = new A();
B b = (B)a; // fails with InvalidCastException
A a = new B();
B b = (B)a; // works OK
所以这与foreach无关,它与你的两个例子做不同的事情有关。要查看foreach
的相同代码失败,请尝试以下操作:
B bCanDo= new B();
bCanDo.Children.Add(new A()); // <-- notice new A() here
foreach (var c in bCanDo.Children)
{
B notExpected = (B)c; // crash
}
因此,虽然您可以将对象引用向下转换为更多派生类型,但只有在引用的实际对象是该派生类型(或者该路径下的更多派生类型)时,这才会起作用。基本上这是一个参考重新解释,你只是在看同一个物体时戴上新眼镜。
如果对象实例不是派生类型,则无法将其转发为更多派生类型,但是,这将是一种转换,需要显式支持或代码才能执行此操作。
答案 1 :(得分:1)
在can not-case中,a包含A类对象,由于A不是B类,因此无法转换为B.
在can-case中,类型B的对象被添加到子节点,因为B是A的子类型,你可以这样做但它仍然是类型B的对象。当你循环子节点并抛出它时到B,你只能这样做,因为它已经是B.你会添加b.Children.Add(new A());
它会再次失败。
答案 2 :(得分:1)
您需要了解的是,强制转换不会以任何方式更改基础对象。
所以,如果我有这些课程:
public class Animal { }
public class Dog : Animal { }
...我写了这段代码:
Dog d = new Dog();
Animal a = (Animal)d;
a
变量仍然是Dog
,它只是作为一个Animal
而被采取行动。
如果我定义了这个类:
public class Cat : Animal { }
...然后我试着写下这段代码:
Dog d = new Dog();
Cat c = (Cat)d;
...我收到错误,但不是因为我无法将Dog
更改为Cat
,而是因为对象d
始终是{{1}我无法将其视为Dog
。 Cat
永远不能是Dog
。
所以在你编写的代码中:
Cat
......同样适用 - A a = new A();
B b = (B)a;//SystemCastInvalidException
永远不能是A
。
但在您的代码中,B
可以为B
。
所以,如果我稍微重写你的代码:
A
...您可以看到即使B bCanDo = new B();
bCanDo.Children.Add(new B());
foreach (A a in bCanDo.Children)
{
B notExpected = (B)a;
}
的{{1}}类型为Children
,您也可以添加bCanDo
类型的子项 - A
< strong>可以成为B
。因此,当您遍历B
时,孩子的类型永远不会改变,即使A
的成员是Children
,如果添加了Children
,您也可以随时进行投射它回到A
。这就是B
演员的作品。