Возникла странная проблема с некоторым кодом C# - метод Getter для свойства отображается как виртуальный, если не отмечен явно.
Проблема проявляется в свойстве DbKey этого класса (код полностью):
public class ProcessingContextKey : BusinessEntityKey, IProcessingContextKey
{
public ProcessingContextKey()
{
// Nothing
}
public ProcessingContextKey(int dbKey)
{
this.mDbKey = dbKey;
}
public int DbKey
{
get { return this.mDbKey; }
set { this.mDbKey = value; }
}
private int mDbKey;
public override Type GetEntityType()
{
return typeof(IProcessingContextEntity);
}
}
Когда я использую отражение для проверки свойства DbKey, я получаю следующий (неожиданный) результат:
Type t = typeof(ProcessingContextKey);
PropertyInfo p = t.GetProperty("DbKey");
bool virtualGetter = p.GetGetMethod(true).IsVirtual; // True!
bool virtualSetter = p.GetSetMethod(true).IsVirtual; // False
Почему для virtualGetter установлено значение True? Я ожидал ложного, учитывая, что свойство не является ни Аннотация, ни виртуальный.
Для полноты - и для удаленной возможности они актуальны, вот объявления для BusinessEntityKey, IProcessingContextKey и IBusinessEntityKey:
public abstract class BusinessEntityKey : IBusinessEntityKey
{
public abstract Type GetEntityType();
}
public interface IProcessingContextKey : IBusinessEntityKey
{
int DbKey { get; }
}
public interface IBusinessEntityKey
{
Type GetEntityType();
}
Заранее спасибо за помощь.
Разъяснение - почему мне это важно?
Мы используем NHibernate и отследили некоторые проблемы с отложенной загрузкой до свойств, которые можно было переопределить только наполовину - виртуального получателя, но частного установщика. После их исправления мы добавили модульный тест, чтобы выявить любые другие места, где это могло произойти:
public void RequirePropertiesToBeCompletelyVirtualOrNot()
{
var properties
= typeof(FsisBusinessEntity).Assembly
.GetExportedTypes()
.Where(type => type.IsClass)
.SelectMany(
type =>
type.GetProperties(
BindingFlags.Instance
| BindingFlags.Public
| BindingFlags.NonPublic))
.Where(property => property.CanRead
&& property.CanWrite)
.Where(property =>
property.GetGetMethod(true).IsVirtual
!= property.GetSetMethod(true).IsVirtual);
Assert.That(
properties.Count(),
Is.EqualTo(0),
properties.Aggregate(
"Found : ",
(m, p) => m + string.Format("{0}.{1}; ",
p.DeclaringType.Name,
p.Name)));
}
Этот модульный тест не работал с упомянутым выше свойством DbKey, и я не понимал, почему.





Он виртуальный, потому что реализует метод интерфейса. Для среды CLR методы реализации интерфейса всегда виртуальны.
Получатель свойства DbKey является виртуальным в IL, потому что он находится в интерфейсе. Сеттер не является виртуальным, потому что он не является частью интерфейса, а является частью конкретного класса.
ECMA-335: Общая языковая инфраструктура Раздел 8.9.4 отмечает, что:
Interfaces can have static or virtual methods, but shall not have instance methods.
Следовательно, получатель, определенный вашим интерфейсом, будет помечен как виртуальный при реализации в производном классе.
Связь к документации, в которой объясняется, что свойства, реализующие интерфейсы, всегда помечаются как виртуальные. Чтобы узнать, действительно ли он виртуальный (поскольку он реализует интерфейс), вам также необходимо проверить, является ли он IsFinal.
Дополнительный вопрос, почему это проблема?