views:

128

answers:

1

I stumbled on a feature of C# method resolution that I didn't notice before. Namely, when I explicitly implement an interface that supports a setter, and the implicit interface only offers a protected set, the compiler sensibly defers to the protected set when I call it. So I get most of the convenience of auto-implemented properties, but I can prevent accidental modification of fields by clients who shouldn't be changing them.

As an example,

 virtual public DateTime CreatedOn { get; protected set; }
 virtual public DateTime? ModifiedOn { get; protected set; }
 #region IHaveUpdateDateFields Members

 DateTime IHaveUpdateDateFields.CreatedOn
 {
    get
    {
        return this.CreatedOn;
    }
    set
    {
        this.CreatedOn = value;
    }
}

DateTime? IHaveUpdateDateFields.ModifiedOn
{
    get
    {
        return this.ModifiedOn;
    }
    set
    {
        this.ModifiedOn = value;
    }
}

Then my model binding code doesn't accidentally set the date, but my ORM event listener can check for entities that implement IHaveUpdateDateFields and set the date when persisting my entity.

My questions are:

  1. Am I relying on defined behavior, or am I guaranteed that all C# compilers will resolve methods this way? I don't want to discover that the C# standard says this kind of method resolution is undefined and then accidentally get a horrifying stack overflow when I build for Mono, for example.
  2. Is there a nicer (ideally terser) way to do this? I could have a ModelBinder-safe interface that I pass to my controller, but that doesn't seem like it would save me code and I don't think it would provide as transparent an approach of minimizing accidental modification of properties.
+3  A: 

That is perfectly well defined; the explicit interface implementations take priority when using the interface, and the regular property takes effect otherwise (including from within the body of the get/set).

As for making it tidier... the best I can offer is to reformat it to make it less verbose...

DateTime IHaveUpdateDateFields.CreatedOn
{
    get { return CreatedOn; }
    set { CreatedOn = value; }
}

(note also that the this is implicit and redundant)

As an aside - the safety is only a convenience, not a guarantee... external callers can still use your interface, and can usually (ab)use reflection to jump past mere things like protected - or even just set the fields directly.

Marc Gravell
Yes, that's my goal... As with most OO constructs, I'm attempting to protect against mistakes, not fraud. The protected set idiom is used for IDs in our system, which of course NHibernate works around using reflection.
JasonTrue