为什么 DbParameterCollection 中的三个属性在引用程序集中是抽象的,而在其他情况下是虚拟的?

Why are three properties in DbParameterCollection abstract in reference assemblies but virtual otherwise?

我正在将一个项目从 project.json 移动到新式 csproj 格式,它包括一个从 DbParameterCollection 派生的 class。在我的真实项目中,我使用的是多目标,但出于这个问题的目的,我们只需要关心 net45.

编译器告诉我必须覆盖三个以前不需要的属性:

如果您点击那些文档链接(适用于 .NET 4.5),您将看到所有属性都是 虚拟的 - 不是抽象的。如果我只是通过调用 csc 来构建代码,一切都很好......只有在使用 .NET Core SDK 时我 运行 才会遇到这个问题。

下面是重现问题的示例代码:

项目文件:

<Project Sdk="Microsoft.NET.Sdk">    
  <PropertyGroup>
    <TargetFramework>net45</TargetFramework>
  </PropertyGroup>    
</Project>

C#代码:

using System;
using System.Collections;
using System.Data.Common;

public class DummyParameterCollection : DbParameterCollection
{
    public override int Count => 0;
    public override object SyncRoot => null;
    public override void Remove(object value) {}
    public override void RemoveAt(int index) {}
    public override void RemoveAt(string parameterName) {}
    public override int Add(object value) => 0;
    public override void Insert(int index, object value) {}
    public override void AddRange(Array values) {}
    public override void Clear() {}
    public override bool Contains(object value) => false;
    public override bool Contains(string value) => false;
    public override void CopyTo(Array array, int index) {}
    public override int IndexOf(object value) => -1;
    public override int IndexOf(string parameterName) => -1;
    protected override DbParameter GetParameter(int index) => null;
    protected override DbParameter GetParameter(string parameterName) => null;
    protected override void SetParameter(int index, DbParameter value) {}
    protected override void SetParameter(string parameterName, DbParameter value) {}
    public override IEnumerator GetEnumerator() => null;
}

错误:

DummyParameterCollection.cs(5,14): error CS0534: 'DummyParameterCollection' does not implement inherited abstract member 'DbParameterCollection.IsSynchronized.get' [c:\Users\skeet\Test\ParameterCollection\ParameterCollection.csproj]
DummyParameterCollection.cs(5,14): error CS0534: 'DummyParameterCollection' does not implement inherited abstract member 'DbParameterCollection.IsFixedSize.get' [c:\Users\skeet\Test\ParameterCollection\ParameterCollection.csproj]
DummyParameterCollection.cs(5,14): error CS0534: 'DummyParameterCollection' does not implement inherited abstract member 'DbParameterCollection.IsReadOnly.get' [c:\Users\skeet\Test\ParameterCollection\ParameterCollection.csproj]

我相信我知道问题的直接原因,但不知道为什么会这样,或者最好的解决方法。

看起来 .NET Core SDK(以及加载此项目时的 VS2017)使用了参考程序集。如果我在 Reflector 中打开 C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Data.dllthat 也会将属性显示为抽象的。而如果我打开 c:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Data.dll,则会显示属性是虚拟的。

我可以通过覆盖属性并从所有属性中返回 false 来解决这个问题 - 但这是处理这种情况的最佳方式吗?除此之外,在这种情况下,参考程序集与真实程序集(和文档)不匹配有什么充分的理由吗?我希望参考程序集是自动生成的,所以 某些 事情像这样不正确是很奇怪的...

参考程序集是正确的。在 .NET Framework 4.5 中,这些属性为 abstract。它们在 .NET Framework 4.5.1 中更改为 virtual。您似乎发现了文档错误。

正如您可能已经猜到的那样,您所观察到的两个 System.Data.dll 程序集之间的差异是由于 .NET Framework 分隔引用程序集和运行时程序集的方式所致。 C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Data.dll 中的参考程序集准确反映了 System.Data.dll 的 4.5 运行时版本中的内容。如果您能够获得一台尚未升级到 .NET Framework 4.5.1 的旧计算机(祝您好运),您会发现 C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Data.dll 中的运行时程序集具有 abstract 中的这些属性. .NET Framework 就地升级。在已升级到 .NET Framework 4.5.1 或更新版本的计算机上,C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Data.dll 已替换为更新版本(具有 virtual,而不是 abstract 属性。)

至于解决方法:为 net451 编译,或者实施虚拟方法是最好的方法。您可以使用其他技巧针对不同版本的 System.Data.dll 进行编译,但我不推荐它

我找不到关于 .NET Framework 4.5 和 4.5.1 之间 API 更改的官方文档,也找不到更改原因的解释,但是,我从 Entity Framework队:https://bugzilla.xamarin.com/show_bug.cgi?id=29167#c0

The following (non-breaking) changes were made to the System.Data APIs in the .NET Framework 4.5.1 release....

The following member were added.

  • System.Data.Common.DbParameter.Precision
  • System.Data.Common.DbParameter.Scale
  • System.Data.SqlClient.SqlConnectionStringBuilder.ConnectRetryCount
  • System.Data.SqlClient.SqlConnectionStringBuilder.ConnectRetryInterval

The following member were changed from abstract to virtual.

  • System.Data.Common.DbDataReader.Close
  • System.Data.Common.DbDataReader.GetSchemaTable
  • System.Data.Common.DbParameter.SourceVersion
  • System.Data.Common.DbParameterCollection.IsFixedSize
  • System.Data.Common.DbParameterCollection.IsReadOnly
  • System.Data.Common.DbParameterCollection.IsSynchronized