从泛型类型显式转换为其类型参数的对象

时间:2014-11-21 17:36:33

标签: c# generics casting

我想创建一个Validatable<T>类,其概念与Nullable<T>类似。我有一个IValidatable接口被许多类型使用,但是如果一个类型还没有实现这个接口,我想让它更容易放置一个包装器,然后使用该包装器来存储验证错误。

我希望能够转换回包装对象,但是即使我有一个显式的运算符集,但是转换失败了(尽管实际上我更喜欢隐式运算符)。这是定义:

    public interface IValidatable
    {
        bool IsValid { get; }
        void Validate();
        // (more validation-related methods here...)
    }

    public class Validatable<T> : IValidatable
        where T : class
    {
        public Validatable(T obj)
        {
            Object = obj;
        }

        public T Object { get; private set; }

        public bool IsValid { get; set; }

        public void Validate()
        {

        }

        public static implicit operator Validatable<T>(T other)
        {
            return new Validatable<T>(other);
        }

        public static explicit operator T(Validatable<T> other)
        {
            return other.Object;
        }

    }

    public static class Validatable
    {
        public static IValidatable AsValidatable<T>(T obj)
            where T: class
        {
            if (obj == null)
                return null;

            var already = obj as IValidatable;

            return already ?? new Validatable<T>(obj);
        }
    }

    public class Person // not IValidatable
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }

但是,如果我尝试:

    public void ConversionTest()
    {
        var person = new Person { FirstName = "Bob", LastName = "Smith" };

        var validatable = Validatable.AsValidatable(person);

        var cast = (Person)validatable; // FAILS here with InvalidCastException
    }

为什么使用InvalidCastException转换为Person失败?

4 个答案:

答案 0 :(得分:3)

Validatable.AsValidatable返回IValidatable,而不是Validatable<T>,而且没有演员。显然,我无法保证IValidatable的实例也是Validatable<T>的实例。

如果您忽略AsValidatable并且在另一个方向上使用您的强制转换操作符,那么它确实有效:

    public static void ConversionTest()
    {
        var person = new Person { FirstName = "Bob", LastName = "Smith" };

        var validatable = (Validatable<Person>)person;           

        var cast = (Person)validatable; // FAILS here with InvalidCastException
    }

对于Nullable他们作弊 - 这是一个特例。

答案 1 :(得分:2)

如果明确说明类型,问题就变得很明显了:

public void ConversionTest()
{
    Personperson = new Person { FirstName = "Bob", LastName = "Smith" };

    IValidatable validatable = Validatable.AsValidatable(person);

    Person cast = (Person)validatable; // FAILS here with InvalidCastException
}

由于validatable具有接口类型,因此无法应用转换运算符(在Validatable<T>中声明)。

Nullable<T>具有特殊的运行时支持以启用此方案。

答案 2 :(得分:1)

这是因为IValidatable无法转换为Person - 只有Validatable。编译器将插入一个简单的强制转换,但不能调用你的运算符,因为它不知道具体的类型。

作为您尝试做的替代方案,您可以选择ComparerIComparable的路线:实施Validator.Default,其中Validator将会IValidatable使用对象自己的{{1}}实现,或构造另一个。这样,就不需要回放对象,因为概念保持独立。

答案 3 :(得分:1)

永远不会为接口评估隐式和显式转换运算符。如果要允许类型IValidatable的引用可转换为某种类型的引用(希望是基础对象的引用),那么IValidatable必须包含一个返回内容[我建议避免使用名称Object]作为System.Object类型的引用,或者使用通用方法T GetContentAs<T>()来尝试将内容作为特定类型返回。两者都不会提供公共类型安全性,但在使用非通用接口时也是如此。

或者,您可以使用类型为IValidatable<out T>的只读Content属性创建协变接口T接口。一个人无法使用强制转换操作符从IValidatable<Foo>提取内容到Foo,但可以使用IValidatable<Foo>.Content属性。