SGEN 在公共泛型方法的 where 子句上失败

本文关键字:where 子句 失败 泛型方法 SGEN | 更新日期: 2023-09-27 18:36:52

>我有一个项目,其中 SGEN 用于预编译序列化程序集(没有/proxytypes 标志)。 在这个项目中,有一个类,到目前为止一直是内部的(因此sgen不会管它)。 我需要公开该类,这样做的简单行为会导致 sgen 失败并出现错误:

类型"Infragistics.Shared.DisposableObject"是在未引用的程序集中定义的..."

此程序集确实被引用,并且始终具有,否则程序集本身将无法编译。版本也完全匹配,并且引用已关闭特定版本。

第一个令人困惑的部分是,这个类没有公共状态(没有公共字段,根本没有属性),这并不能使其成为序列化的良好候选者。

更令人困惑的部分是,删除唯一公共(泛型)方法上的 where 子句允许 sgen 很好地处理程序集。

这是具有唯一公共的、非显式实现的东西的类(为接口实现了两种方法,不相关):

public class AppointmentDrawFilter : IUIElementDrawFilter
{
    // This is a fluent method to register a type with its handler
    public AppointmentDrawFilter Support<TUiElement>(DrawPhase phases, Func<UIElement, Appointment> retriever = null)
        where TUiElement : UIElement  // <-- commenting out (or changing UIElement to MarshalByRefObject) fixes sgen
    {
        // adds input to a couple dictionaries (failure still occurs with body commented out)
        return this;
    }
}

注意:UIElement 继承自 DisposableObject,sgen 在失败时找不到这种类型。 请注意,当我注释掉 where 子句时,UIElement 仍在其他地方使用,但 sgen 对此并不满意。 将 Support() 方法标记为内部也允许 sgen 完成,因为它只关心公共内容。

为什么 sgen 首先会关心非 Web 服务公共方法?

为什么它只会绊倒 where 子句的存在?

为什么在这种特殊情况下,当组件明显存在时,sgen 找不到它?

SGEN 在公共泛型方法的 where 子句上失败

我对此进行了一些测试,并找到了您可以使用的可能解决方法。

我发现,当您有一个泛型方法时,可以重现这种情况,该方法具有一个 where 子句,该子句引用来自当前程序集中没有任何派生类型的类型(或 where 子句引用的类型的基本类型)。

例如,我发现的问题的最简单重现是:

具有以下 C# 代码的类库 One:

namespace One
{
    public class BaseObject
    {
    }
}

具有以下 C# 代码的类库 2:

using One;
namespace Two
{
    public class TestClass
    {
        public void TestMethod<T>()
            where T : BaseObject
        {
        }        
    }
}

在 Two 上使用 sgen 时.dll会重现错误,它抱怨程序集 One 未被引用。

对于解决方法,我发现我可以从程序集 1 中的类派生程序集(类库)2 中的任何类,或者从程序集 1 中实现接口。 若要在特定情况下解决此问题,可以添加以下类:

using Infragistics.Shared;
namespace DayViewTextCenter
{
    public class WorkaroundSgenIssue:DisposableObject
    {
        protected override void OnDispose()
        {
        }
    }
}

虽然我实际上没有找到您提出的三个问题的答案,但我确实发现实际错误是 System.Xml.Serialization.Compiler 类的 Compile 方法中的 System.InvalidOperationException:

System.InvalidOperationException occurred
  Message=Unable to generate a temporary class (result=1).
error CS0012: The type 'One.BaseObject' is defined in an assembly that is not referenced. You must add a reference to assembly 'One, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
  Source=System.Xml
  StackTrace:
       at System.Xml.Serialization.Compiler.Compile(Assembly parent, String ns, XmlSerializerCompilerParameters xmlParameters, Evidence evidence)
  InnerException: 

我能够从上面重现这个引用程序集 1 和 2,并使用以下代码:

Type type = typeof(TestClass);
Assembly assembly = type.Assembly;
XmlReflectionImporter importer = new XmlReflectionImporter();
XmlTypeMapping mapping = importer.ImportTypeMapping(type);
CompilerParameters parameters = new CompilerParameters();
Assembly xmlAssembly = XmlSerializer.GenerateSerializer(new Type[] { type }, new XmlMapping[] { mapping }, parameters);

与我之前建议的解决方法一样,如果添加了派生自程序集 One 中的类的第二个类型,则可以避免异常。

程序集 2 所需的其他类:

public class Test : BaseObject
{
}

更新了用于生成序列化程序集的逻辑:

Type type = typeof(TestClass);
Assembly assembly = type.Assembly;
XmlReflectionImporter importer = new XmlReflectionImporter();
XmlTypeMapping mapping = importer.ImportTypeMapping(type);
Type type2 = typeof(Test);
XmlReflectionImporter importer2 = new XmlReflectionImporter();
XmlTypeMapping mapping2 = importer2.ImportTypeMapping(type2);
CompilerParameters parameters = new CompilerParameters();
Assembly xmlAssembly = XmlSerializer.GenerateSerializer(new Type[] { type, type2 }, new XmlMapping[] { mapping, mapping2 }, parameters);