如何检测哪种 .NET 语言正在调用我的代码

How to detect which .NET language is calling my code

我正在构建一个生成用户代理字符串的库,该字符串报告一些漂亮的数据,例如 OS 版本和 currently installed .NET Framework versions。我很好奇:

是否可以通过编程方式检测哪种语言正在调用我的库?或者源语言一旦编译成 CIL 就完全不透明了?

不幸的是,这是不可能的,正如您通过提及 CIL 所确定的那样。

我能想到两种可能的解决方案:

  1. 但是,如果相应的 .NET dll(调用代码的程序集)的 PDB 文件可用,那么您可以检查该文件以确定所使用的语言。然而,这是一个很大的 "IF",我会远离这样的解决方案。
  2. 检查调用程序集引用的程序集。有时(很少)可能会引用特定于语言的程序集。

编辑:我把它变成了一个small library,它封装了一些启发式算法,便于调用。

我想出了一个启发式方法,它似乎可以很好地满足我自己的需要。

@Don 的回答和这些问题给了我一些提示:

  • Decompiled DLL - Clues to help tell whether it was C# or VB.NET?
  • Decompiling VB.Net assembly produces code with invalid member variable names; names starting with $STATIC$

注意事项

  • 仅区分 VB.NET 和 C#,不区分任何其他 CLR 语言。如果没有足够的 VB.
  • 证据,则假定 C#
  • 这是一种有根据的猜测,因此误报的可能性 > 0。
  • 一些提示基于编译器实现细节,这些细节可能会发生变化。
  • 这似乎也适用于 Mono,但 YMMV。
  • 它是昂贵的反射,所以在现实生活中你会想要将它包装在 Lazy<> 或其他一些机制中以确保它只被调用一次。
  • 如@HABO 所述,这可能是也可能不是非常有用的信息。我主要是想看看它是否可以完成。

var lang = DetectAssemblyLanguage(Assembly.GetCallingAssembly());

public static string DetectAssemblyLanguage(Assembly assembly)
{
    var referencedAssemblies = assembly
        .GetReferencedAssemblies()
        .Select(x => x.Name);

    var types = assembly
        .GetTypes();

    // Biggest hint: almost all VB.NET projects have a
    // hidden reference to the Microsoft.VisualBasic assembly
    bool referenceToMSVB = referencedAssemblies.Contains("Microsoft.VisualBasic");

    // VB.NET projects also typically reference the special
    // (YourProject).My.My* types that VB generates
    bool areMyTypesPresent = types.Select(x => x.FullName).Where(x => x.Contains(".My.My")).Any();

    // If a VB.NET project uses any anonymous types,
    // the compiler names them like VB$AnonymousType_0`1
    bool generatedVbNames = types.Select(x => x.Name).Where(x => x.StartsWith("VB$")).Any();

    // If a C# project uses dynamic, it'll have a reference to Microsoft.CSharp
    bool referenceToMSCS = referencedAssemblies.Contains("Microsoft.CSharp");

    // If a C# project uses any anonymous types,
    // the compiler names them like <>f__AnonymousType0`1
    bool generatedCsNames = types.Select(x => x.Name).Where(x => x.StartsWith("<>")).Any();

    var evidenceForVb = new bool[] 
    {
        referenceToMSVB,
        myTypesPresent,
        vbGeneratedNames
    };

    var evidenceForCsharp = new bool[] {
        true, // freebie. ensures ties go to C#
        referenceToMSCS,
        csGeneratedNames
    };

    var scoreForVb = evidenceForVb.Count(x => x)
                     - evidenceForCsharp.Count(x => x);

    // In the case of a tie, C# is assumed
    return scoreForVb > 0
        ? "vb"
        : "cs";
}