I am trying to check if a property exists in a 3rd party dll (for backward compatibility) before trying to access, why does the if
statement returns true and then fail with exception Method not found: 'Boolean Shared.Models.Account.get_EnsureTextField()'
when I access the property?
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
}
Note: code compiles fine as at compile time version that indeed has EnsureTextField
is used, the issue is that code fails at run-time (when different version of the same assembly is loaded) even if I have if
check which based on my understanding should prevent the exception.
Fundamentally, trying to run code which has been built against a library which has later changed in a backwardly-incompatible way is a bad idea. While you may be able to work around it as shown below, I wouldn't be surprised to see quirks in all kinds of places, and the workarounds may become more and more difficult to manage. If at all possible it would be better to take a step back and try to change your dependency management process to avoid this entirely.
Having said which, I can see why it's failing: the JIT compiler isn't able to JIT-compile your method, because it can't find a method reference (get_EnsureText
) which is used within the method. It's failing before it even evalutes the if
condition.
Here's a small example to demonstrate this. Start with Library.cs
:
public class Account
{
public string Name { get; set; }
}
Compile that to Library.dll
.
Then write Program.cs:
using System;
class Program
{
static void Main(string[] args)
{
var account = new Account();
if (DateTime.Now.Hour == 1000)
{
Console.WriteLine(account.Name);
}
}
}
Note how we're never going to enter the body of the if
statement, because it will never be 1000 o'clock.
Compile that, referring to Library.dll
.
Next, comment out Account.Name
in Library.cs
and recompile just Library.dll
, then rerun Program.exe
:
Unhandled Exception: System.MissingMethodException: Method not found: 'System.String Account.get_Name()'.
at Program.Main(String[] args)
We can work around that by stopping the JIT compiler from ever trying to access the property if it's not going to actually execute it:
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);
}
}
Go through the same dance as before, and this code now executes without an exception.
Now that wasn't using reflection... but we can change it easily to do so. Change Library.cs
to give the account a name by default:
public class Account
{
public string Name { get; set; } = "Default name";
}
Then change Program.cs
to use the property only if it's present - but only to even call a method that refers directly to the property if we've checked it:
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}");
}
}
Going through the same dance as before, but running the code each time, we initially get output of:
Account name: Default name
But after removing the property and recompiling, the output becomes:
Account.Name is missing
... which is what you wanted.
This only works because the JIT compiler is compiling method-by-method though. I don't know of any guarantee that it will do so, instead of speculatively compiling all the methods within a type and failing if any of them has issues. So it's a pretty fragile solution, but it might at least be a temporary workaround for you.
Alternative approach
As mentioned in question comments, you could use dynamic typing to avoid the property reference being embedded directly into the IL. Here's a slightly shorter alternative to the code above:
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");
}
}
}
That's certainly briefer, and probably more robust in terms of not relying on JIT-compiler implementation details. On the other hand, it's more prone to typos etc, because the d.Name
expression isn't being checked at all at compile-time. (Indeed, the Program.cs
code will still compile even against the new version of the library.)
Side-note
This code only tests that there is a property. It could be an initially read/write property but then become write-only, in which case we'll still try to call the get accessor, and fail. The condition you're checking can be modified to handle this reasonably easily though.