使用泛型投射

时间:2012-05-08 17:00:30

标签: c# generics

我在尝试访问实现类的接口属性时遇到了问题。问题是,我在运行时只有特定的(Cat)类型,因此我的应用程序在尝试强制转换时会中断。

这就是我所拥有的:

public class Animal {}
public class Cat : Animal {}

public interface IPetSitter {}
public interface IPetSitter<T> IPetSitter where T : Animal {
    T Pet { get; set; }
}

public class Kid { }

public class NeighborhoodKid : Kid, IPetSitter<Animal> {
    Animal Pet { get; set; }
}

// --- Implementation ---

// Kid Timmy is instantiated elsewhere
// Animal type "A" is passed in dynamically

if (Timmy is IPetSitter) {
    ((IPetSitter<A>)Timmy).Pet = new A();
}

如果类型不匹配,则此演员表会出错。我喜欢做这样的事情:

public interface IPetSitter {
    object Pet { get; set; }
}

public interface IPetSitter<T> : IPetSitter where T : Animal {
    new T Pet { get; set; }
}

// --- Implementation ---

NeighborhoodKid Timmy = new NeighborhoodKid();
((IPetSitter)Timmy).Pet = new Cat();

但这迫使任何实现IPetSitter的东西同时具有[object Pet]和[Cat Pet]属性。

我很欣赏任何想法。感谢。

更新:我最初应该更清楚,但有时我会创建一个Kid类,有时候会创建一个NeighborhoodKid类。这就是我需要施展到IPetSitter<T>的原因。并非我创造的所有孩子都会坐着宠物。这开始听起来令人毛骨悚然。

4 个答案:

答案 0 :(得分:0)

最后,蒂米被初始化为NeighborhoodKid。这意味着Pet,对他来说,就像Animal一样。 Timmy是IPetSitter<Animal>,您无法将其投放到IPetSitter<Cat>

你可以反过来做,假设Cat:Animal。

此:

((IPetSitter<Animal>)Timmy).Pet = new Cat();

实际上只是因为Timmy真的是IPetSitter<Animal>,因为NeighborhoodKid:IPetSitter<Animal>,所以你并没有真正做任何事情 - 只是访问pet属性。

之后的行问题是,没有访问Pet,也没有将Cat放入其中 - 它将Timmy投射到IPetSitter<Cat>这就是问题所在。你正在贬低它不是它的东西。

您始终可以向上投射,但只能向下投射到初始化对象的位置。

如果你想让NeighborhoodKid成为任何动物的IPetSitter,包括动物本身,你应该这样做:

public class NeighborhoodKid<T> : IPetSitter<T> where T : Animal
{
    ...
}

这样,它是通用的,并将其限制为动物或源自Animal的东西,无论是直接还是间接。

但是,如果您将其初始化为new NeighborhoodKid<Animal>(),则无法将其视为(也称为转换为)IPetSitter<Cat>,因为它被初始化为IPetSitter<Animal>(因为为NeighborhoodKid构造函数提供的泛型T参数是Animal,并传递给IPetSitter泛型参数。)

答案 1 :(得分:0)

问题在于你定义了

public class NeighborhoodKid : IPetSitter<Animal>
{
    Animal IPetSitter<Animal>.Pet { get; set; }
}

而不是

public class NeighborhoodKid : IPetSitter<Cat>
{
    Cat IPetSitter<Animal>.Pet { get; set; }
}

public class NeighborhoodKid<T> : IPetSitter<T> where T : Animal
{
    Cat IPetSitter<T>.Pet { get; set; }
}

答案 2 :(得分:0)

为什么不只是Timmy.Pet = new Cat();? 只需将其设为public即可全部设置:

public class NeighborhoodKid : Kid, IPetSitter<Animal>
{
   public Animal Pet { get; set; }
}

如果你创建一个不从IPetSitter继承的NeighborhoodKid,那么setter就不可用了。

public class LazyNeighborhoodKid : Kid
{
   // Nothing here, hes not a Pet Sitter, can access Pet
}

答案 3 :(得分:0)

除了他们在收藏中的实用性之外,我并不真正喜欢泛型,这就是原因。您强制每个NeighborhoodKid绑定到一个特定类型的Animal。如果蒂米可以看猫或狗怎么办?你打算为每个人创建不同的Timmy实例吗?

相反,我认为您在实例级别强制执行动物类型。例如(为了简洁起见,我已经截断了一些类型):

public interface IAnimal {...}

public class Cat : IAnimal {...}

public interface IPetSitter
{
    IAnimal Pet { get; set; }
}

public class Kid : IPetSitter
{
    public Kid (params Type[] allowedPets) {
        _allowedPets = allowedPets;
    }

    readonly IEnumerable<Type> _allowedPets;

    IAnimal _pet;
    public IAnimal Pet 
    {
        get {
            return _pet;
        }
        set {
            if (!_allowedPets.Contains(value.GetType()) {
                throw new InvalidArgumentException("This instance does not support " + value.GetType().Name + ".");
            }
            _pet = value;
        }
    }
}

如果您将执法留在实例级别,那么您不一定需要使用具体的强制转换来设置属性。