Reputation: 27740
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);
}
Upvotes: 25
Views: 17806
Reputation: 23315
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.
Upvotes: 1
Reputation: 6440
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);
}
Upvotes: 2
Reputation: 887195
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);
Upvotes: 19
Reputation: 62367
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;
Upvotes: 6
Reputation: 23315
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.
Upvotes: 1
Reputation: 6465
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)
{
...
}
Upvotes: 2
Reputation: 24212
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);
}
Upvotes: 5
Reputation: 74530
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.
Upvotes: 5