views:

588

answers:

8

Is there anyway to pass the property of an Object by reference? I know I can pass the whole object but I want to specify a property of the object to set and check it's type so I know how to parse. Should I maybe take another approach (I cannot change the original object in anyway)?

public class Foo{
    public Foo(){}
    public int Age { get; set; }
}

private void setFromQueryString(object aProperty, String queryString, HttpContext context)
{
    //here I want to handle pulling the values out of 
    //the query string and parsing them or setting them
    //to null or empty string...
    String valueString = context.Request.QueryString[queryString].ToString(); 

    //I need to check the type of the property that I am setting.

    //this is null so I can't check it's type
    Type t = aProperty.GetType();
}

private void callingMethod(HttpContext context)
{
    Foo myFoo = new Foo();
    setFromQueryString(myFoo.Age, "inputAge", context);
}
+4  A: 

You could wrap the property with a corresponding methods and delegates and pass the delegates.

delegate int IntGetter<T>(T obj);
delegate void IntSetter<T>(T obj, int value);

int GetAge(Foo foo)
{
    return foo.Age;
}

void SetAge(Foo foo, int value)
{
    foo.Age = value;
}

private void callingMethod(HttpContext context)
{
    Foo myFoo = new Foo();
    // need to also pass foo so the property can be set
    setFromQueryString(new IntSetter<Foo>(SetAge), foo, "inputAge", context);
}

private void setFromQueryString<T>(
    IntSetter<T> intSetter, 
    T obj, 
    String queryString, 
    HttpContext context)
{
    String valueString = context.Request.QueryString[queryString].ToString(); 
    intSetter(T, valueString);
}
Zach Johnson
+8  A: 

You can call the function with a lambda expression:

private void setFromQueryString<T>(Action<T> setter, String queryString, HttpContext context) 
{ 
    //here I want to handle pulling the values out of  
    //the query string and parsing them or setting them 
    //to null or empty string... 
    String valueString = context.Request.QueryString[queryString].ToString();  

    //I need to check the type of the property that I am setting. 

    //this is null so I can't check it's type 
    Type t = typeof(T); 
    ...
    setter(value);
} 

You would call it like this:

setFromQueryString<int>(i => myFoo.Age = i, "inputAge", context);

EDIT: If you really want type inference:

private void setFromQueryString<T>(Func<T> getter, Action<T> setter, String queryString, HttpContext context) {
    ...
}
setFromQueryString(() => myFoo.Age, i => myFoo.Age = i, "inputAge", context);
SLaks
This looks awesome. I've implmented the method however the call to it is giving "The type arguments for method '...' Cannot be inferred from usage. Try specifying the type arguments explicitly."
ctrlShiftBryan
This is very very close but it's forcing me to do this...setFromQueryString((int i) => myFoo.Age = i, "inputAge", context);I'd like it to infer the type.
ctrlShiftBryan
Ok this makes sense. I needs the getter to be able to infer the type. Thanks.
ctrlShiftBryan
+2  A: 

No, there is no way to directly pass the property by reference. Visual Basic offers this support in the language by putting the value of the property into a temp variable, and then that is passed by reference and reassigned upon return.

In C#, you can only approximate this behavior by passing a Func<T> to get the property value, and an Action<T> for setting the value (using closures), where T is the type of the property.

casperOne
+3  A: 

Why not use generics and return the object?

private T setFromQueryString<T>(String queryString, HttpContext context)
{
    String valueString = context.Request.QueryString[queryString].ToString(); 

    // Shouldn't be null any more
    Type t = typeof(T);
}

private void callingMethod(HttpContext context)
{
    Foo myFoo = new Foo();
    myFoo.Age = setFromQueryString<int>("inputAge", context);
}

Not quite sure why you're so set on inference, but given that you are, you could do this

private void setFromQueryString(ref T aProperty, String queryString, HttpContext context)
{
    String valueString = context.Request.QueryString[queryString].ToString(); 

    // Shouldn't be null any more
    Type t = typeof(T);
}

private void callingMethod(HttpContext context)
{
    Foo myFoo = new Foo();
    setFromQueryString(ref myFoo.Age, "inputAge", context);
}
pdr
This is one approach but I don't want to specify the type "<int>" in the call. That is should be inferred from what is passed in.
ctrlShiftBryan
Pretty sure it will be inferred in C#3
pdr
Nope same problem as with SLaks solution. It wont infer the type.
ctrlShiftBryan
How about this then?
pdr
+1  A: 

Passing function with lambda is probably most elegant, but if you just want a simple solution to your problem

private void callingMethod(HttpContext context)
{
    Foo myFoo = new Foo();
    int myAge = myFoo.Age;
    setFromQueryString(ref myAge, "inputAge", context);
    myFoo.Age = myAge;    
}

private void setFromQueryString(ref int age, String queryString, HttpContext context)
{
...
}
Fadrian Sudaman
+1  A: 

Why are you making it so complicated? You know the type of the property at compile time, just do it the easy way with one line of code:

Foo.Age = int.Parse(context.Request.QueryString["Parameter"]);

If you need to check the type, just add a little function that wraps int.TryParse() and returns an innocuous result (e.g. 0) if you get "pdq" in the querystring value instead of a number.

Robert C. Barth
Of course this is the normal way to do it but I'm trying to make something more reusable. I want to see if there is a way remove the redundancy of the int.Parse as well as encapsulate some other error handling logic.
ctrlShiftBryan
I understand that, but you're sacrificing quite a bit of readability and maintainability for not really anything; there's no code reuse because it's one line of code, and you're not making it any more elegant or easier to code, IMO.A really elegant solution, one that I use, is to create classes (derived from System.Web.UI.Page) for your web pages that contain the query string parameters as properties and set them in an OnLoad override, this way they are available as this.PropertyName in the code behind page, and do your type-checking in that discreet class. I'll post another answer...
Robert C. Barth
+1  A: 

As others have pointed out, you can do this using delegates, using one of the many ways to specify delegates. However, if you intend to do this regularly, you should consider creating a wrapper type for passing properties by reference that wraps the delegates required, it may create a nicer API.

For example:

class PropertyReference<T>
{
   public T Value
   {
       get
       {
           return this.getter();
       }

       set
       {
           this.setter(value);
       }
   }

   public PropertyReference(Func<T> getter, Action<T> setter)
   {
      this.getter = getter;
      this.setter = setter;
   }
}

That way you can pass around a reference to your property and modify it by setting the reference value.

var reference = new PropertyReference(
                        () => this.MyValue,
                        x => this.MyValue = x);

reference.Value = someNewValue;
Jeff Yates
+1  A: 

Here's a totally different solution for you:

Create classes derived from System.Web.UI.Page that have the QueryString parameters as properties. Also, using a utility function (see ConvertType, below), you don't need to do too much to get the data out of the QueryString. Lastly, inside those derived classes, define a static inner class that holds constants that are the names of the QueryString parameters so that you don't need to reference any magic values anywhere.

I usually define a base page class for my project, which makes it a convenient place to do common things that happen on all pages, as well as a few utility functions:

public class MyBasePage : System.Web.UI.Page
{

  public T GetQueryStringValue<T>(
        string value,
        T defaultValue,
        bool throwOnBadConvert)
  {
    T returnValue;

    if (string.IsNullOrEmpty(value))
      return defaultValue;
    else
      returnValue = ConvertType<T>(value, defaultValue);

    if (returnValue == defaultValue && throwOnBadConvert)
      // In production code, you'd want to create a custom Exception for this
      throw new Exception(string.Format("The value specified '{0}' could not be converted to type '{1}.'", value, typeof(T).Name));
    else
      return returnValue;
  }

  // I usually have this function as a static member of a global utility class because
  // it's just too useful to only have here.
  public T ConvertType<T>(
        object value,
        T defaultValue)
  {
    Type realType = typeof(T);

    if (value == null)
      return defaultValue;

    if (typeof(T) == value.GetType())
      return (T)value;

    if (typeof(T).IsGenericType)
      realType = typeof(T).GetGenericArguments()[0];

    if (realType == typeof(Guid))
      return (T)Convert.ChangeType(new Guid((string)value), realType);
    else if (realType == typeof(bool))
    {
      int i;
      if (int.TryParse(value.ToString(), out i))
        return (T)Convert.ChangeType(i == 0 ? true : false, typeof(T));
    }

    if (value is Guid && typeof(T) == typeof(string))
      return (T)Convert.ChangeType(((Guid)value).ToString(), typeof(T));

    if (realType.BaseType == typeof(Enum))
      return (T)Enum.Parse(realType, value.ToString(), true);

    try
    {
      return (T)Convert.ChangeType(value, realType);
    }
    catch
    {
      return defaultValue;
    }
  }
}

public class MyPage : MyBasePage
{
  public static class QueryStringParameters
  {
    public const string Age= "age";
  }

  public int Age
  {
    get 
    { 
     return base.GetQueryStringValue<int>(Request[QueryStringParameters.Age], -1);
    }
  }
}

Then, in your regular page, in the code behind, it now looks like this:

public partial class MyWebPage : MyPage
{
  protected void Page_Load(object sender, EventArgs e)
  {
    Foo myFoo = new Foo();
    Foo.Age = this.Age;
  }
}

It makes the code behind classes very clean (as you can see), and it's easily maintainable because all the heavy-lifting is done by two functions (GetQueryStringValue and ChangeType) that are reused in each of the page classes, and everything is type-safe (you'll notice in GetQueryStringValue that you can specify whether the function throws if the value can't be converted or just uses returns default value; both are appropriate at different times, depending on your app).

Furthermore, you can even easily write a VS plugin or CodeSmith script to generate the derived page class quite easily. And there aren't a bunch of delegates and stuff being passed around, which I find new developers have a hard time understanding.

Robert C. Barth