Cast ISomeInterface <nullable <bool>&gt;到ISomeInterface <object> </object> </nullable <bool>

时间:2013-10-15 15:22:06

标签: c# asp.net-mvc interface casting nullable

我有一个自定义的HtmlHelper,我试图获取泛型类型属性的值。 我的ViewModel具有ChangeRequestFormField类型的属性。我的ViewModel,类/接口和html助手的相关部分如下所示。

在我的帮助器中,我需要从ViewModel属性访问IsRequired和ValueHasChanged属性。这适用于ChangeRequestFormField。但是当我到达ChangeRequestFormField时,我收到以下错误:

  

无法投射类型的对象   'StaffChanges.Models.ChangeRequestFormField 1[System.Nullable 1 [System.Boolean]]'   输入   'StaffChanges.Models.IChangeRequestFormField`1 [System.Object的]'。

错误发生在帮助器的这一行:

var isRequired = ((IChangeRequestFormField<object>)metadata.Model).IsRequired;

也许我正在接近这个错误,但是我需要一种方法来访问帮助器中的那些属性,直到运行时才知道ChangeFormField中的类型。

视图模型:

public class JobChangeModel
{
    public ChangeRequestFormField<string> Reason1 { get; set; }
    public ChangeRequestFormField<bool?> IsTransferEventNeeded { get; set; }
}


public class ChangeRequestFormField<T> : IChangeRequestFormField<T>
{
    public ChangeRequestFormField(string formFieldType, T fieldValue, T originalValue)
    {
        this.FieldValue = fieldValue;
        this.OriginalValue = originalValue;

        switch (formFieldType)
        {
            case FormFieldTypes.DoNotRender:
                this.RenderField = false;
                this.IsRequired = false;
                break;

            case FormFieldTypes.Required:
                this.RenderField = true;
                this.IsRequired = true;
                break;

            case FormFieldTypes.Optional:
                this.RenderField = true;
                this.IsRequired = false;
                break;

            default:
                this.RenderField = false;
                this.IsRequired = false;
                break;
        }
    }

    public T FieldValue { get; set; }

    public bool IsRequired { get; private set; }

    public T OriginalValue { get; set; }

    public string OriginalValueString
    {
        get
        {
            return this.OriginalValue == null ? string.Empty : this.OriginalValue.ToString();
        }
    }

    public bool ValueHasChanged
    {
        get
        {
            return !EqualityComparer<T>.Default.Equals(this.FieldValue, this.OriginalValue);
        }
    }
}


public interface IChangeRequestFormField<out T>
{
    bool IsRequired { get; }

    string OriginalValueString { get; }

    bool ValueHasChanged { get; }
}

public static MvcHtmlString LabelForChangeRequestFormField<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string labelText, IDictionary<string, object> htmlAttributes)
{
    if (expression.Body.Type.GetGenericTypeDefinition() != typeof(ChangeRequestFormField<>))
    {
        return html.LabelFor(expression, htmlAttributes);
    }

    var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
    var isRequired = ((IChangeRequestFormField<object>)metadata.Model).IsRequired;
    var valueChanged = ((IChangeRequestFormField<object>)metadata.Model).ValueHasChanged;

    // other code left out
}

1 个答案:

答案 0 :(得分:1)

根据您的代码,它看起来不像IChangeRequestFormField接口需要是通用的。如果从接口声明中删除类型参数T,则可以将所有派生的泛型类强制转换为非泛型接口。

public interface IChangeRequestFormField
{
    bool IsRequired { get; }
    string OriginalValueString { get; }
    bool ValueHasChanged { get; }
}

public class ChangeRequestFormField<T> : IChangeRequestFormField
{
    // ...
}

然后,您可以像这样使用它:

var isRequired = ((IChangeRequestFormField)metadata.Model).IsRequired;

如果您需要界面中的泛型类型,事情会变得更复杂。然后,您需要小心如何实现界面的协变或逆变方面,以支持您所需的铸造行为。看看this article on MSDN

注意

特别是,您的协变接口不起作用的原因是因为存在协变类型必须是引用类型的限制。由于Nullable<T>不是引用类型,因此转换失败。

如果您发现确实需要协变行为,则可以实现自己的可空引用类型来包装值类型,例如boolint