views:

67

answers:

5

I'm beginning to look into custom attributes, and I've come up with the following idea: what if I could make an attribute which would restrict the use of a variable to the property it backed?

[RestrictToProperty("Foo")]
private object _foo;
public object Foo
{
    get { return _foo; }
    set
    {
        _foo = value;
        OnFooChanged(EventArgs.Empty);
    }
}
public object NotFoo
{
    get { return _foo; }  // Warning
    set { _foo = value; } // Warning
}
public void Bar()
{
    _foo = new object();  // Warning
}

// Warning: 'MyClass._foo' should not be used outside of property 'Foo'

I believe it's possible, because Obsolete does a similar thing.

[Obsolete]
private object _foo;
public void Bar()
{
    _foo = new object(); // Warning: 'MyClass._foo' is obsolete
}

Unfortunately, I have no idea how to go about it, and can't find much beyond simple runtime attribute tutorials. Is this possible? If so, where would I start?

+2  A: 

You should be able to write an FxCop rule that would make this either an error or a warning.

Daniel Plaisted
A: 
stakx
Just to make sure you understand my question - the warning would be a compiler warning at design/compile time, like `Obsolete` warnings - not an exception, or anything during runtime.
Daniel Rasmussen
RE your PS: I simplified the example to make it less cluttered. My real code raises changed events from the `Set` methods which I don't want to accidentally circumvent.
Daniel Rasmussen
_@Daniel Rasmussen:_ Fair enough. I still don't see any other way apart from declaring your attribute class and writing some Visual Studio extension (if you want warnings at "design time") or a custom build process (if you want warnings at compile time).
stakx
+3  A: 

No, that is not possible. ObsoleteAttribute has a special mention in the C# specification regarding how the compiler itself responds to it.

You could implicity restrict a variables use to a single property by using auto implemented properties.

public class Test
{
  public object Foo { get; set; }
}

Edit: If you wanted special logic handled independently in the getter and setter you could try the following code. This seems awfully obnoxious to me though.

public class Test
{

  private PrivateMembers Members { get; set; }

  public object Foo
  {
    get
    {
      return Members.Foo;
    }
    set
    {
      Members.Foo = value;
      // Do something else here.
    }
  }

  private class PrivateMembers
  {
    public object Foo { get; set; }
  }
}
Brian Gideon
That's disappointing. See my comment to stakx about auto-implemented properties.
Daniel Rasmussen
@Daniel: I edited my answer to provide another, albiet obnoxious, alternative.
Brian Gideon
I'm not sure how this does what I want... You'd still be able to access `Members.Foo` without the `Foo` property, wouldn't you..?
Daniel Rasmussen
@Daniel: True, it definitely doesn't do exactly what you want.
Brian Gideon
+1  A: 

Unfortunately this isn't possible. You might want to explore PostSharp though, which is an Aspect-Oriented IL-weaver for .NET--basically it lets your source-code go through an extra layer of compilation which can inject all the extra ceremony.

STW
+1  A: 

Slightly off-topic but you could (ab)use the Obsolete attribute to achieve what you need.

Mark the backing field as obsolete so that the compiler generates a warning when you try to access it, and then suppress those warnings within the property getter/setter. (The relevant warnings are CS0612 and CS0618.)

[Obsolete("Backing field should not be used outside of property")]
private object _foo;

public object Foo
{
    #pragma warning disable 612, 618
    get { return _foo; }
    set
    {
        _foo = value;
        OnFooChanged(EventArgs.Empty);
    }
    #pragma warning restore 612, 618
}

This does feel like a nasty hack, and really isn't something that I'd recommend. There are other, preferable alternatives. For example, custom FxCop rules, unit tests, good code commenting etc.

LukeH
Yeah, I'm not really interesting in a hack for it. I was just wondering if I could come up with an elegant solution. Thanks for the idea, though. It's a good example of what you *can* do.
Daniel Rasmussen
That's actually pretty clever.
Brian Gideon