将 ISomeInterface> 转换为 ISomeInterface

本文关键字:ISomeInterface object 转换 bool Nullable | 更新日期: 2023-09-27 18:35:22

我有一个自定义的HtmlHelper,我试图在其中获取泛型类型属性的值。My 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
}

将 ISomeInterface<Nullable<bool>> 转换为 ISomeInterface<object>

根据您的代码,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;

如果您需要在界面中使用泛型类型,事情会变得更加复杂。然后,您需要小心如何实现接口的协变或逆变方面,以支持所需的转换行为。看看这篇关于 MSDN 的文章。

注意

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

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

相关文章: