c# - 为什么元组中的接口(interface)不能用作 ASP.NET Razor 部分 View 中的模型?

标签 c# asp.net razor tuples partial-views

我有一个 ASP.NET Razor Pages 项目,其部分 View 使用元组类型作为其模型。元组元素之一使用接口(interface)作为其类型。当使用接口(interface)类型的对象调用分部 View 时,它可以工作。但是当使用实现该接口(interface)的类调用它时,它会抛出异常。

我创建了一个 simplified repo重现此错误。

该项目定义了一个带有类实现的接口(interface):

public interface ISampleInterface
{
    int Value { get; }
}

public class ImplementationClass : ISampleInterface
{
    public int Value => 5;
}

在页面中,我可以创建类型为接口(interface)或实现接口(interface)的类的对象:

public class IndexModel : PageModel
{
    // type is the interface
    public ISampleInterface InterfaceInstance { get; } = new ImplementationClass();

    // type is an implementation of the interface
    public ImplementationClass ClassInstance { get; } = new ImplementationClass();

    public void OnGet() { }
}

然后有一个部分 View ,它使用包含接口(interface)的元组作为其模型:

@model (ISampleInterface Sample, int Number)

<p>Sample value: @Model.Sample.Value </p>
<p>Unrelated number: @Model.Number </p>

但是引用分部 View 时,可以使用接口(interface)类型的对象,而类类型的对象会抛出异常:

<!-- This works: -->
<partial name="_SampleTuplePartial" model="(Model.InterfaceInstance, 1)" />

<!-- This throws an exception: -->
<partial name="_SampleTuplePartial" model="(Model.ClassInstance, 1)" />

第一个部分标记按预期呈现,但第二个部分标记抛出此异常:

InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'System.ValueTuple<code>2[ImplementationClass,System.Int32]', but this ViewDataDictionary instance requires a model item of type 'System.ValueTuple</code>2[ISampleInterface,System.Int32]'.

两个对象都实现了该接口(interface),那么为什么第二个对象会抛出异常?

只有当模型是元组时才会发生这种情况。仅以接口(interface)作为模型的部分 View 可以与任一类型的对象一起使用:

@model ISampleInterface

<p>Sample value: @Model.Value</p>
<partial name="_SampleInterfacePartial" model="Model.InterfaceInstance" />
<partial name="_SampleInterfacePartial" model="Model.ClassInstance" />

最佳答案

TL;DR:因为ValueTuple不是协变的。

Covariance enables you to use a more derived type than that specified by the generic parameter

out (generic modifier)

该问题与泛型类型参数有关,而不是分部 View 本身。

例如,给出以下代码:

public static void Main(string[] args)
{
    var strings = new List<string>();
    // this is fine because IEnumerable is covariant
    DoSomething(strings);

    // compile error: cannot convert from 'System.Collections.Generic.List<string>' to 'System.Collections.Generic.List<object>'
    DoAnotherThing(strings);
}

static void DoSomething(IEnumerable<object> objects)
{
}

static void DoAnotherThing(List<object> objects)
{
}

IEnumerable泛型类型是协变的,它被声明为 IEnumerable<out T> 。这意味着您可以转换 IEnumerable<string>IEnumerable<object>因为string源自object 。但你不能施放List<string>List<object>因为List<T>不是协变的(无论如何都不可能)

ValueTuple也不是协变的,所以你不能强制转换 ValueTuple<ImplementationClass, int>ValueTuple<ISampleInterface, int>甚至艰难ImplementationClass实现ISampleInterface

关于c# - 为什么元组中的接口(interface)不能用作 ASP.NET Razor 部分 View 中的模型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72232605/

相关文章:

asp.net - 我该如何解决 : the type or namespace name 'DbModelBuilder' Could not be found

asp.net datalist 选择每个类别的所有复选框

asp.net - Akamai 存在 ASPX 回发问题

javascript - 重载返回View的Controller时,如何根据ViewBag属性加载不同的内容?

c# - 在 C# 中保留 C++ 指针是否安全?

c# - 在数据表中拆分并创建新行的场景?

c# - 如何使用 .pem 文件中提供的公钥验证签名?

c# - 在不创建对象 C# 的情况下获取编码消息的长度(以字节为单位)

asp.net-mvc - Razor 对引用的 html 属性进行编码

c# - 使用 while 循环时显示多行结果而不是一行