Looking for a more elegant way to perform custom get/set functionality on class properties

Viewed 213

I'm trying to find a way to refine some code that I have. I work with a 3rd party API that has a REALLY complicated API request object (I'll call it ScrewyAPIObject) that has tons of repetition in it. Every time you want to set a particular property, it can take a page worth of code. So I built a library to provide a simplified wrapper around the setting/getting of its properties (and to handle some value preprocessing).

Here's a stripped-down view of how it works:

public abstract class LessScrewyWrapper
{
    protected ScrewyAPIRequest _screwy = new ScrewyAPIRequest();

    public void Set(string value)
    {
        Set(_getPropertyName(), value);
    }

    public void Set(string property, string value)
    {
        // Preprocess value and set the appropriate property on _screwy. This part 
        // has tons of code, but we'll just say it looks like this:
        _screwy.Fields[property] = "[" + value + "]";
    }

    protected string _getPropertyName()
    {
        // This method looks at the Environment.StackTrace, finds the correct set_ or 
        // get_ method call and extracts the property name and returns it.
    }

    public string Get()
    {
        // Get the property name being access
        string property = _getPropertyName();

        // Search _screwy's structure for the value and return it. Again, tons of code,
        // so let's just say it looks like this:
        return _screwy.Fields[property];
    }

    public ScrewyAPIRequest GetRequest()
    {
      return _screwy;
    }
}

Then I have a child class that represents one specific type of the screwy API request (there are multiple kinds that all have the same structure but different setups). Let's just say this one has two string properties, PropertyA and PropertyB:

public class SpecificScrewyAPIRequest : LessScrewyWrapper
{
  public string PropertyA
  {
    get { return Get(); }
    set { Set(value); }
  }
  public string PropertyB
  {
    get { return Get(); }
    set { Set(value); }
  }
}

Now when I want to go use this library, I can just do:

SpecificScrewyAPIRequest foo = new SpecificScrewyAPIRequest();
foo.PropertyA = "Hello";
foo.PropertyB = "World";
ScrewyAPIRequest request = foo.GetRequest();

This works fine and dandy, but there are different kinds of data types, which involves using generics in my Set/Get methods, and it just makes the child classes look a little kludgy when you're dealing with 50 properties and 50 copies of Get() and Set() calls.

What I'd LIKE to do is simply define fields, like this:

public class SpecificScrewyAPIRequest : LessScrewyWrapper
{
  public string PropertyA;
  public string PropertyB;
}

It would make the classes look a LOT cleaner. The problem is that I don't know of a way to have .NET make a callback to my custom handlers whenever the values of the fields are accessed and modified.

I've seen someone do something like this in PHP using the __set and __get magic methods (albeit in a way they were not intended to be used), but I haven't found anything similar in C#. Any ideas?

EDIT: I've considered using an indexed approach to my class with an object-type value that is cast to its appropriate type afterwards, but I'd prefer to retain the approach where the property is defined with a specific type.

2 Answers

Maybe in your case DynamicObject is a suitable choice:

public class ScrewyDynamicWrapper : DynamicObject
{
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        // get your actual value based on the property name

        Console.WriteLine("Get Property: {0}", binder.Name);
        result = null;
        return true;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        // set your actual value based on the property name

        Console.WriteLine("Set Property: {0} # Value: {2}", binder.Name, value);
        return true;
    }
}

And define your wrapper objects:

public class ScrewyWrapper
{
    protected dynamic ActualWrapper = new ScrewyDynamicWrapper();

    public int? PropertyA
    {
        get { return ActualWrapper.PropertyA; }
        set { ActualWrapper.PropertyA = value; }
    }

    public string PropertyB
    {
        get { return ActualWrapper.PropertyB; }
        set { ActualWrapper.PropertyB = value; }
    }
}

However, you can't rely on the property type inside ScrewyDynamicWrapper with this approach, so it depends on your actual API requirements - maybe it won't work for you.

Instead of fields, If you define as property in class, It will be more easy.

public class SpecificScrewyAPIRequest
{
    public string PropertyA { get; set; }
    public string PropertyB { get; set; }
}

Then you can create extension generic method to return ScrewyAPIRequest object.

public static class Extensions
{
    public static ScrewyAPIRequest GetRequest<T>(this T obj)
    {
        ScrewyAPIRequest _screwy = new ScrewyAPIRequest();

        var test= obj.GetType().GetProperties();
        foreach (var prop in obj.GetType().GetProperties())
        {
            _screwy.Fields[prop.Name] = prop.GetValue(obj, null);
        }
        return _screwy;
    }
}

Now you can easily get ScrewyAPIRequest from any class object.

Your code will look like following.

        SpecificScrewyAPIRequest foo = new SpecificScrewyAPIRequest();
        foo.PropertyA = "Hello";
        foo.PropertyB = "World";
        ScrewyAPIRequest request = foo.GetRequest();
Related