gpt4 book ai didi

c# - 当外部库不向后兼容时检查属性是否存在

转载 作者:行者123 更新时间:2023-11-30 23:03:22 25 4
gpt4 key购买 nike

我试图在尝试访问之前检查一个属性是否存在于第 3 方 dll 中(为了向后兼容),为什么 if 语句返回 true 然后失败并出现异常 Method未找到:“Boolean Shared.Models.Account.get_EnsureTextField()” 当我访问该属性时?

var type = am.account.GetType(); //account is of type class Account

//See if Account does not have property EnsureTextField
if(type.GetProperty("EnsureTextField") != null)
{
cbEnsureTextField.Checked = am.account.EnsureTextField; //Exception thrown
}

注意:代码编译正常,因为确实使用了 EnsureTextField 的编译时版本,问题是代码在运行时失败(当加载同一程序集的不同版本时),即使根据我的理解,我有 if 检查应该防止异常。

最佳答案

从根本上说,尝试运行针对后来以向后不兼容的方式更改的库构建的代码是一个坏主意。虽然您可以如下所示解决它,但如果在各种地方看到怪癖,我不会感到惊讶,并且解决方法可能会变得越来越难以管理。如果有可能,最好退后一步并尝试更改您的依赖项管理流程以完全避免这种情况。

话虽如此,我明白了失败的原因:JIT 编译器无法对您的方法进行 JIT 编译,因为它找不到使用的方法引用 (get_EnsureText)方法内。它甚至在评估 if 条件之前就失败了。

这里有一个小例子来证明这一点。从 Library.cs 开始:

public class Account
{
public string Name { get; set; }
}

将其编译为 Library.dll

然后编写Program.cs:

using System;

class Program
{
static void Main(string[] args)
{
var account = new Account();
if (DateTime.Now.Hour == 1000)
{
Console.WriteLine(account.Name);
}
}
}

请注意我们如何永远进入if 语句的主体,因为它永远不会是 1000 点钟。

编译它,引用Library.dll

接下来,注释掉Library.cs中的Account.Name,重新编译Library.dll,然后重新运行Program.exe :

Unhandled Exception: System.MissingMethodException: Method not found: 'System.String Account.get_Name()'.
at Program.Main(String[] args)

如果 JIT 编译器不会实际执行它,我们可以通过阻止 JIT 编译器尝试访问该属性来解决这个问题:

using System;

class Program
{
static void Main(string[] args)
{
var account = new Account();
if (DateTime.Now.Hour == 1000)
{
PrintAccountName(account);
}
}

static void PrintAccountName(Account account)
{
Console.WriteLine(account.Name);
}
}

跳完和之前一样的舞,这段代码现在可以毫无异常地执行。

现在没有使用反射...但我们可以很容易地改变它来这样做。更改 Library.cs 以默认为帐户命名:

public class Account
{
public string Name { get; set; } = "Default name";
}

然后更改 Program.cs 以仅在属性存在时使用该属性 - 但如果我们检查了它,甚至只调用直接引用该属性的方法:

using System;

class Program
{
static void Main(string[] args)
{
var account = new Account();
if (account.GetType().GetProperty("Name") != null)
{
PrintAccountName(account);
}
else
{
Console.WriteLine("Account.Name is missing");
}
}

static void PrintAccountName(Account account)
{
Console.WriteLine($"Account name: {account.Name}");
}
}

经历与之前相同的舞蹈,但每次都运行代码,我们最初得到的输出是:

Account name: Default name

但是删除属性并重新编译后,输出变为:

Account.Name is missing

...这就是您想要的。

有效,因为 JIT 编译器是逐个方法编译的。我不知道它会这样做的任何保证,而不是推测性地编译一个类型中的所有方法,如果其中任何一个有问题就会失败。所以这是一个非常脆弱的解决方案,但它至少对您来说是一个临时的解决方法。

替代方法

如问题评论中所述,您可以使用动态类型来避免将属性引用直接嵌入到 IL 中。下面是上述代码的一个略短的替代方案:

using System;

class Program
{
static void Main(string[] args)
{
var account = new Account();
if (account.GetType().GetProperty("Name") != null)
{
// Avoid a compile-time reference to the property
dynamic d = account;
Console.WriteLine($"Account name: {d.Name}");
}
else
{
Console.WriteLine("Account.Name is missing");
}
}
}

就不依赖 JIT 编译器实现细节而言,这肯定更简洁,而且可能更可靠。另一方面,它更容易出现拼写错误等,因为 d.Name 表达式在编译时根本 没有被检查。 (事实上​​ ,即使针对新版本的库,Program.cs 代码仍然可以编译。)

旁注

此代码仅测试是否有 属性。它可能是一个最初的读/写属性,但后来变成只写的,在这种情况下,我们仍然会尝试调用 get 访问器,但会失败。不过,可以修改您正在检查的条件以合理轻松地处理此问题。

关于c# - 当外部库不向后兼容时检查属性是否存在,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49959862/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com