C#运行时将约束泛型类型转换为约束类型

时间:2017-04-25 20:03:05

标签: c# generics interface casting

我遇到类似于以下代码的运行时转换错误。首先,我有一个设置了访客模式的界面:

public interface IAnimalVisitor<out T>
{
    T Visit(Dog a);
    T Visit(Cat a);
}

public interface IAnimal
{
    string Name { get; }

    T Accept<T>(IAnimalVisitor<T> v);
}

public abstract class AnimalBase : IAnimal
{
    public string Name { get; }

    protected AnimalBase(string name)
    {
        Name = name;
    }

    public abstract T Accept<T>(IAnimalVisitor<T> v);
}

public class Dog : AnimalBase
{
    public Dog(string name) : base(name) { }

    public override T Accept<T>(IAnimalVisitor<T> v)
    {
        return v.Visit(this);
    }
}

public class Cat : AnimalBase
{
    public Cat(string name) : base(name) { }

    public override T Accept<T>(IAnimalVisitor<T> v)
    {
        return v.Visit(this);
    }
}

然后实现访问者模式的类(隐藏为嵌套类):

public class AnimalSpeaker
{
    private class SpeakerVisitor : IAnimalVisitor<string>
    {
        public string Visit(Dog a)
        {
            return "Woof";
        }

        public string Visit(Cat a)
        {
            return "Meow";
        }
    }

    private readonly SpeakerVisitor _SpeakerVisitor = new SpeakerVisitor();

    public string Speak(IAnimal a)
    {
        return a.Accept(_SpeakerVisitor);
    }
}

最后我有一个泛型类被约束为使用IAnimals类型,它通过封装类将一个实例传递给访问者:

public abstract class AnimalSignTextBuilderBase<TAnimal>
    where TAnimal : IAnimal
{
    private readonly AnimalSpeaker _AnimalSpeaker = new AnimalSpeaker();

    public string BuildSignText(TAnimal a)
    {
        var spokenText = _AnimalSpeaker.Speak(a);
        return $"{a.Name} says {spokenText}.";
    }
}

public class DogSignTextBuilder : AnimalSignTextBuilderBase<Dog> { }
public class CatSignTextBuilder : AnimalSignTextBuilderBase<Cat> { }

此代码编译完全正常,但在运行时(从ASP.net请求调用它)我打电话

new DogSignTextBuilder().BuildSignText(new Dog("Fido"))

我收到了无效的强制转换异常。这是因为调用_AnimalSpeaker.Speak(a)

我不知道为什么会这样。此外,我可以在Visual Studio中调试代码并在即时窗口中输入a is IAnimal,从而生成true。我也可以在即时窗口中输入_AnimalSpeaker.Speak(a),这会导致以下错误:

error CS1503: Argument 1: cannot convert from 'TAnimal' to 'IAnimal'

我也可以把这个例子发布到dotnetfiddle中并让它工作正常,所以在这一点上,我完全不知道会发生什么事情。

修改:我还发现,在即时窗口中调用a.Name会因以下原因而失败:

error CS1061: 'TAnimal' does not contain a definition for 'Name' and no extension method 'Name' accepting a first argument of type 'TAnimal' could be found (are you missing a using directive or an assembly reference?)

我检查了我的参考资料,他们都在那里。到目前为止,似乎在运行时忽略了泛型约束。我也可以输入(a as IAnimal).Name,这会正确返回Fido

1 个答案:

答案 0 :(得分:0)

这最终成为一个与我认为与之相关的一切完全无关的问题。缺点是代表未能正确地转换值,而不是通用输入。