Seth
Seth

Reputation: 984

Generically wrapping API with explicit methods by type

I'm looking for a way to easily manage decisions based on type, or sets of types. (Example, if is string, double, etc do one thing, if is enum do something else, etc). I have a cross section of both importance AND maintainability being critical.

I am working with an API that forces the following convention.

double dblField;
if(!apiObj.IsNull("NameOfDOubleProperty"))
  dblefield = apiObj.GetDouble("NameOfDOubleProperty");

string stringField;
if(!apiObj.IsNull("NameOfStringProperty"))
   stringField = apiObj.GetDouble("NameOfStringProperty");

 //Repeat for each property

I LOATHE magic strings all over code. If I change something in the app I am reading this from, then I have to ocme add another one, or worse remember to rename every place.

Step1: On start up, get readonly static fields (generated by a single time reflection call). I have that.

Step 2: My question - if it is a string, double, or other common primitive, i have a generic apiobj.GetField that I can cast. However, in the case of some objects, i need special handling.

Thoughts or patterns?

Upvotes: 0

Views: 45

Answers (2)

alek kowalczyk
alek kowalczyk

Reputation: 4936

You could use actual property names using expressions, with a helper method:

    public static string GetPropertyName<T>(Expression<Func<T, object>> expression)
    {
        var body = expression.Body;
        if (body is UnaryExpression)
        {
            var unaryExpression = body as UnaryExpression;
            if (unaryExpression.Operand is MemberExpression)
                return (unaryExpression.Operand as MemberExpression).Member.Name;
        }
        if (body is MemberExpression)
        {
            return (body as MemberExpression).Member.Name;
        }
        return null;
    }

Then you could make such call

 double dblField;
 if(!apiObj.IsNull(GetPropertyName<YourObject>(o=>o.NameOfDoubleProperty))
     dblefield = apiObj.GetDouble(GetPropertyName<YourObject>(o=>o.NameOfDoubleProperty));

or you could just use the GetPropertyName method in your api, i.e.

dblefield = apiObj.GetDouble<YourObject>(o=>o.NameOfDoubleProperty);

that way you won't have any magic strings and can use the actual property names, which will make refactoring and maintaining a lot easier.

Then for Step 2 you can have:

dblefield = apiObj.GetField<YourObject, double>(o=>o.NameOfDoubleProperty);

and inside your GetField<TObj, TType>(.....):

if(gottenValue is TType)
    return (TType)gottenValue;

you can get the type of the property YourObject.NameOfDoubleProperty from the expression with following method:

public static Type GetPropertyType<T>(Expression<Func<T, object>> expression)
{
    var body = expression.Body;
    MemberInfo mi = null;
    if (body is UnaryExpression)
    {
        var unaryExpression = body as UnaryExpression;
        if (unaryExpression.Operand is MemberExpression)
            mi = (unaryExpression.Operand as MemberExpression).Member;
    }
    else if (body is MemberExpression)
    {
        mi = (body as MemberExpression).Member;
    }

    if(mi is PropertyInfo)
    {
        return (mi as PropertyInfo).PropertyType;
    }
    return null;
}

But I don't know how you could use the type in a usefull manner, i.e. you can cast to the returned type by Reflection (dunno about performance though)

dbleField = Convert.ChangeType(yourValue, GetPropertyType<YourObject>(o=>o.NameOfDoubleProperty);

or have this functionality in your api method - however then this method must return an object as we don't know what type it will be - that is why I like the approach with two generic parameters more.

Upvotes: 2

Sam Axe
Sam Axe

Reputation: 33738

I'd consider converting your string literals to constants..

const string NAME_OF_DOUBLE_PROPERTY = "NameOfDOubleProperty";
const string NAME_OF_STRING_PROPERTY = "NameOfStringProperty";

That, at a minimum, will help with the typos.

By your Step1 above, I assume this is a list of all the fields that are set to receive data from the API.

I think I would start by creating a custom attribute to decorate the fields:

[ApiProperty(NAME_OF_DOUBLE_PROPERTY)]
double dblField;

That way the api property name and the receiving field are kept together. Now, in basically one spot, you have the receiving field, name of the api property, and the type (via the FieldInfo class and its CustomAttributes property from your Step1).

The rest should be quite trivial for you.

Upvotes: 2

Related Questions