C#通过引用传递属性

时间:2010-03-03 21:06:56

标签: c# .net properties pass-by-reference

无论如何通过引用传递Object的属性?我知道我可以传递整个对象,但是我想指定要设置的对象的属性并检查它的类型,以便我知道如何解析。我是否可以采取另一种方法(无论如何我都无法改变原始物体)?

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);
}

8 个答案:

答案 0 :(得分:18)

您可以使用lambda表达式调用该函数:

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);
} 

你会这样称呼:

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

编辑:如果确实想要类型推断:

private void setFromQueryString<T>(Func<T> getter, Action<T> setter, String queryString, HttpContext context) {
    ...
}
setFromQueryString(() => myFoo.Age, i => myFoo.Age = i, "inputAge", context);

答案 1 :(得分:5)

您可以使用相应的方法和委托包装该属性并传递委托。

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);
}

答案 2 :(得分:5)

不,没有办法通过引用直接传递属性。 Visual Basic通过将属性的值放入临时变量,然后通过引用传递并在返回时重新分配,在语言中提供此支持。

在C#中,您只能通过传递Func<T>来获取属性值来近似此行为,并使用Action<T>来设置值(使用闭包),其中T是该物业的类型。

答案 3 :(得分:5)

正如其他人所指出的那样,您可以使用委托来执行此操作,使用多种方法之一来指定委托。但是,如果您打算定期执行此操作,则应考虑创建一个包装类型,以便通过引用传递属性来包装所需的委托,它可以创建更好的API。

例如:

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;
   }
}

通过这种方式,您可以传递对属性的引用,并通过设置引用值来修改它。

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

reference.Value = someNewValue;

答案 4 :(得分:2)

为什么不使用泛型并返回对象?

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);
}

不太确定为什么你这么干预,但鉴于你是,你可以这样做

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);
}

答案 5 :(得分:2)

使用lambda传递函数可能是最优雅的,但如果你只是想要一个简单的解决方案来解决你的问题

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)
{
...
}

答案 6 :(得分:1)

你为什么这么复杂?你知道编译时属性的类型,只需用一行代码就可以轻松实现:

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

如果你需要检查类型,只需添加一个包含int.TryParse()的小函数,如果在查询字符串值而不是数字中得到“pdq”,则返回一个无害的结果(例如0)。

答案 7 :(得分:1)

这是一个完全不同的解决方案:

创建从System.Web.UI.Page派生的类,其中QueryString参数作为属性。此外,使用实用程序函数(请参阅下面的ConvertType),您无需执行太多操作即可从QueryString中获取数据。最后,在这些派生类中,定义一个静态内部类,它保存作为QueryString参数名称的常量,这样就不需要在任何地方引用任何魔术值。

我通常为我的项目定义一个基页类,这使得它可以方便地在所有页面上执行常见操作,以及一些实用程序功能:

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);
    }
  }
}

然后,在常规页面中,在后面的代码中,它现在看起来像这样:

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

它使得非常类后面的代码变得干净(正如你所看到的),并且它很容易维护,因为所有繁重工作都是由两个函数(GetQueryStringValue和ChangeType)完成的,每个函数都在每个函数中重用页面类,一切都是类型安全的(你会注意到GetQueryStringValue你可以指定函数是否抛出,如果值无法转换或只使用返回默认值;两者都适用于不同的时间,取决于你的应用程序)。

此外,您甚至可以轻松编写VS插件或CodeSmith脚本来轻松生成派生页面类。并且没有一大堆代表和东西被传递,我发现新开发人员很难理解。