views:

588

answers:

3

Having a strange issue with some C# code - the Getter method for a property is showing up as virtual when not explicitly marked.

The problem exhibits with the DbKey property on this class (code in full):

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);
    }
}

When I use reflection to inspect the DbKey property, I get the following (unexpected) result:

Type t = typeof(ProcessingContextKey);
PropertyInfo p = t.GetProperty("DbKey");
bool virtualGetter = p.GetGetMethod(true).IsVirtual; // True!
bool virtualSetter = p.GetSetMethod(true).IsVirtual; // False

Why does virtualGetter get set to True? I expected false, given that the property is neither abstract nor virtual.

For completeness - and for the remote possibilty they are relevant, here are the declarations for BusinessEntityKey, IProcessingContextKey, and IBusinessEntityKey:

public abstract class BusinessEntityKey : IBusinessEntityKey
{
    public abstract Type GetEntityType();
}

public interface IProcessingContextKey : IBusinessEntityKey 
{
    int DbKey { get; }
}

public interface IBusinessEntityKey
{
    Type GetEntityType();
}

Thanks in advance for your help.

Clarification - why does this matter to me?

We're using NHibernate and traced some issues with lazy loading to properties that were only half overridable - virtual getter but private setter. After fixing these up, we added a Unit test to catch any other places where this could occur:

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)));
}

This unit test was failing on the DbKey property mentioned above, and I didn't understand why.

+9  A: 

It's virtual because it implements an interface method. Interface implementation methods are always virtual as far as the CLR is concerned.

JaredPar
+4  A: 

The DbKey property getter is virtual in the IL because it is in an interface. The setter is not virtual because it is not part of the interface but part of the concrete class.

ECMA-335: Common Language Infrastructure Section 8.9.4 notes that:

Interfaces can have static or virtual methods, but shall not have instance methods.

Therefore the getter defined by your interface will be marked virtual when implemented in a derived class.

sixlettervariables
+2  A: 

Link to the documentation that explains that properties that implement interfaces are always marked virtual. To see if it is really virtual (since it implements an interface) you'll also need to check if it IsFinal.

tvanfosson