C#/.NET 泛型和 Cdecl Varargs 错误?

Posted

技术标签:

【中文标题】C#/.NET 泛型和 Cdecl Varargs 错误?【英文标题】:C#/.NET Generics and Cdecl Varargs Bug? 【发布时间】:2011-08-16 08:51:29 【问题描述】:

为什么Foo() 成功但Bar() 抛出BadImageFormatException

using System.Runtime.InteropServices;
using System.Text;

static class Program

    [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
    static extern int sprintf([Out] StringBuilder buf, string format, __arglist);

    static void Main(string[] args)
    
        Foo<int>(2); //Runs fine
        Bar<int>(2); //Error: "The signature is incorrect"
    

 static void Foo<T>(int a)  sprintf(new StringBuilder(8), "%d", __arglist(a)); 
 static void Bar<T>(T   a)  sprintf(new StringBuilder(8), "%d", __arglist(a)); 

【问题讨论】:

【参考方案1】:

试试这样:

using System;
using System.Runtime.InteropServices;
using System.Text;

static class Program

    [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
    static extern int sprintf([Out] StringBuilder buf, string format, params object[] args);

    static void Main(string[] args)
    
        Foo(2);
        Bar<int>(2);
    

    static void Foo(int a)  sprintf(new StringBuilder(8), "%d", a); 
    static void Bar<T>(T a) sprintf(new StringBuilder(8), "%d", a); 

【讨论】:

@Mehrdad,好吧,您正在使用一个未记录的功能,今天它不适用于泛型,明天它可能不适用于其他东西,因为它没有记录,所以没有官方答案。 C#中使用变长参数的方式是使用params关键字。 如果我使用动态 IL 生成(记录在案)对您有什么影响吗? (我不知道这是否会出错,但至少它会摆脱这个“它没有记录”的问题......) @Mehrdad,我仍在尝试找出您要解决的实际问题是什么以及为什么 params 关键字对您不起作用? 从技术上讲,这不再是一个“问题”,因为我找到了解决方法,但它仍然困扰着我,我想知道为什么它首先会发生。【参考方案2】:

您在问为什么未记录的 (_arglist,_makeref,_reftype,_refvalue) 关键字不起作用。

你应该问微软:D

如果你真的想知道我对此的看法,可能是因为泛型在编译时不知道 T 的类型,但它们被编译为类。 __arglist 在编译时需要什么,这是一个谜。因为在声明泛型的行中,不要指定 __arglist 的参数类型。

但所有这些都与使用 C# 中的 sprintf 一样晦涩难懂...至少如果它是 _snwprintf_s 或类似的:D

【讨论】:

@Marino:不确定您所说的“编译时”是什么意思…….NET 中的编译时间几乎没有意义; JIT-time就是事情变得重要的时候,那个时候T肯定有一个类型... @Marino:它们完全一样,除了 int32/!!T 在方法中恰好有 1 个位置不同。 但第二个泛型类 id 编译错误...不是 sprintf 失败,而是泛型类无效..您甚至无法输入泛型方法,...您可能发现了应该报告给微软的错误 @Marino:不知道你所说的“编译错误”是什么意思......你能详细说明一下吗?应该是什么? 这意味着 IL 代码无法运行,而不是 sprintf 无法运行...应该是什么你应该问微软,我不知道文档中没有说明什么,但是我什至无法调试进入第二个泛型方法...【参考方案3】:

如下代码所示,编译成native代码时,方法在运行前抛出异常:

var t = typeof(Program);
var m = t.GetMethod("Bar", BindingFlags.NonPublic | BindingFlags.Static);
m = m.MakeGenericMethod(typeof(int));
System.Runtime.CompilerServices.RuntimeHelpers.PrepareMethod(m.MethodHandle); //error

它还抱怨传递给 SizeOf 的类型错误。我猜这可能是 CLR 中的一个错误,导致它将内部句柄传递给泛型类型参数,而不是传递给方法的真实类型的句柄。

【讨论】:

以上是关于C#/.NET 泛型和 Cdecl Varargs 错误?的主要内容,如果未能解决你的问题,请参考以下文章

泛型和 Com Visible .NET 库

C# 泛型和类型检查

使用C#中的自定义数据模型实现泛型和扩展ObservableCollection方法

C# 泛型和方法

为什么从另一个泛型方法调用的泛型varargs方法返回Object []? [重复]

需要在泛型和子类代码中相互引用的 C++ 类