C# 即使条件为真,条件的假部分也已解决

C# 即使条件为真,条件的假部分也已解决,c#,reflection,dll,C#,Reflection,Dll,我最近不得不更改一段代码,以允许与旧版本的DLL兼容。DLL具有相同的名称且未签名。不同之处还在于添加到新DLL的一些附加方法 一种方法是在项目中引用新的DLL,构建并运行它,这在我看来是不对的。如果要使用另一个DLL,只需将其替换到bin文件夹中即可。您可以通过使用反射检查构造函数中某个地方是否存在方法来避免错误,并设置一个标志,以便以后在使用旧版本时可以避免调用新函数 奇怪的是,在使用旧版本时,以下代码不起作用: int[] someVariable = (DLLIsNewFormat) ?

我最近不得不更改一段代码,以允许与旧版本的DLL兼容。DLL具有相同的名称且未签名。不同之处还在于添加到新DLL的一些附加方法

一种方法是在项目中引用新的DLL,构建并运行它,这在我看来是不对的。如果要使用另一个DLL,只需将其替换到bin文件夹中即可。您可以通过使用反射检查构造函数中某个地方是否存在方法来避免错误,并设置一个标志,以便以后在使用旧版本时可以避免调用新函数

奇怪的是,在使用旧版本时,以下代码不起作用:

int[] someVariable = (DLLIsNewFormat) ? DLL.CallNewMethod() : new int[5];
基本上,发生的情况是DLLIsNewFormat为False,但由于某种原因,我得到了错误:

找不到方法:“Int32[][NameSpace].[Class].CallNewMethod”


我知道最好的方法是检查每个函数是否存在,然后使用反射调用它们。但我不知道为什么代码会这样。这只是未定义的行为吗?

您想要的是隐藏对JIT中不存在的方法的调用

为此,您需要确保在函数中进行的每个不存在的调用以及对该函数的调用都由版本条件控制:

private int[] WrappedNewMethod()
{
  return DLL.CallNewMethod();
}

...SomeOtherMethod()
{
   int[] someVariable = (DLLIsNewFormat) ? WrappedNewMethod(): new int[5];
}

这是在JIT编译包含代码段的方法时发生的。为了实现JIT编译,该方法需要在调用该方法时可用。由于该方法不可用,JIT编译器在调用包含此代码的方法时,甚至在执行该方法之前,都会引发此异常

一种解决方法是定义一种新方法:

int[] HideCall()
{
    return DLL.CallNewMethod();
}
然后直接调用此方法,而不是DLL.CallNewMethod

更好的解决方案是在程序集中定义一个接口,该接口由条件DLL和有条件使用此DLL的程序集引用。在主程序集中提供此接口的默认实现,在有条件使用的DLL中提供备用实现

然后,在运行时,您只需查看DLL是否可用,使用反射构造实现此接口的类的实例,然后用此实例替换对默认实现的引用

示例代码:

// Interface, in an assembly visible to both of the other assemblies.
public interface IDLLInterface
{
    int[] CallNewMethod();
}

// Implementation in the main program.
class DefaultDLLImplementation : IDLLInterface
{
    public int[] CallNewMethod()
    {
        return new int[5];
    }
}

static class DLLImplementation
{
    public readonly IDLLInterface Instance;

    static DLLImplementation()
    {
        // Pseudo-code
        if (DllIsAvailable) {
            Instance = ConstructInstanceFromDllUsingReflection();
        } else {
            Instance = new DefaultDLLImplementation();
        }
    }
}
然后可以改用DLLImplementation.Instance.CallNewMethod,并自动调用正确的方法


当然,我建议您使用更具描述性的名称来命名接口,以便清楚地知道它的含义。

Hmm……我明白了。你能解释一下为什么JIT会尝试解析对该函数的调用吗?这是某种形式的优化吗?分支预测?因为毕竟JIT只编译需要的部分代码,对吗?@FarhadAliNoo JIT单独编译的最小单元是一种方法。因此,方法内的所有调用都必须可用,才能被潜在调用,但除非实际调用,否则这些方法本身不需要JIT。霍伊的回答也有解释。谢谢你的回答阿列克谢。我真的没有投票权,否则我会投票给你哦,是的,我明白为什么现在要解决这个问题了。谢谢你的回答。界面解决方案看起来很简洁,我会注意到这一点,以便以后使用。不幸的是,我无法更改以前编译的dll版本。我不能用这种方法。隐藏呼叫或一路进行反射似乎是唯一的选择。我真的不喜欢隐藏方法,因为我认为这会让代码变得丑陋,因为大约有10个新方法,我必须有10个虚拟方法来隐藏它们。另一方面,反射可能对性能有影响,但也可能是可以接受的。@FarhadAliNoo作为一种替代方法,您可以在主程序集中实现外部DLL类,并有条件地构造它的实例,而使用默认实现。