向下转换类和foreach变量之间的区别

时间:2016-04-01 11:33:51

标签: c# inheritance casting

我有这样的课程:

// 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? } 投降?是的,这在逻辑上都是正确的,但我可以在哪里阅读有关它的信息?

3 个答案:

答案 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}我无法将其视为DogCat永远不能是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演员的作品。