C#通过引用传递属性

C#通过引用传递属性,c#,.net,properties,pass-by-reference,C#,.net,Properties,Pass By Reference,是否仍然可以通过引用传递对象的属性?我知道我可以传递整个对象,但是我想指定对象的属性来设置和检查它的类型,这样我就知道如何解析。我是否应该采取另一种方法(无论如何我都不能更改原始对象) 可以使用相应的方法和委托包装属性,并传递委托 delegate int IntGetter<T>(T obj); delegate void IntSetter<T>(T obj, int value); int GetAge(Foo foo) { return foo.Age;

是否仍然可以通过引用传递对象的属性?我知道我可以传递整个对象,但是我想指定对象的属性来设置和检查它的类型,这样我就知道如何解析。我是否应该采取另一种方法(无论如何我都不能更改原始对象)


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

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);
}
delegate int IntGetter(T obj);
委托无效IntSetter(T对象,int值);
int GetAge(Foo-Foo)
{
返回foo.Age;
}
无效设置(Foo-Foo,int值)
{
foo.年龄=价值;
}
私有void调用方法(HttpContext上下文)
{
Foo myFoo=新Foo();
//还需要传递foo,以便可以设置属性
setFromQueryString(新的IntSetter(SetAge),foo,“inputAge”,context);
}
私有void setFromQueryString(
IntSetter IntSetter,
T obj,
字符串查询字符串,
HttpContext(上下文)
{
String valueString=context.Request.QueryString[QueryString].ToString();
intSetter(T,valueString);
}

您可以使用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);
} 
private void setFromQueryString(操作设置器、字符串queryString、HttpContext)
{ 
//这里我想处理从中提取值的问题
//查询字符串并对其进行分析或设置
//要为空字符串或空字符串。。。
String valueString=context.Request.QueryString[QueryString].ToString();
//我需要检查正在设置的属性的类型。
//这是空的,所以我不能检查它的类型
类型t=类型(t);
...
设定器(值);
} 
你可以这样称呼它:

setFromQueryString<int>(i => myFoo.Age = i, "inputAge", context);
public partial class MyWebPage : MyPage
{
  protected void Page_Load(object sender, EventArgs e)
  {
    Foo myFoo = new Foo();
    Foo.Age = this.Age;
  }
}
setFromQueryString(i=>myFoo.Age=i,“输入”,上下文);
编辑:如果确实需要类型推断:

private void setFromQueryString<T>(Func<T> getter, Action<T> setter, String queryString, HttpContext context) {
    ...
}
setFromQueryString(() => myFoo.Age, i => myFoo.Age = i, "inputAge", context);
private void setFromQueryString(Func getter、Action setter、String queryString、HttpContext上下文){
...
}
setFromQueryString(()=>myFoo.Age,i=>myFoo.Age=i,“输入”,上下文);

否,无法通过引用直接传递属性。VisualBasic在该语言中提供了这种支持,它将属性的值放入一个临时变量中,然后通过引用传递该值,并在返回时重新赋值


在C#中,您只能通过传递一个
Func
来获得属性值,并传递一个
Action
来设置值(使用闭包),其中
T
是属性的类型。

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

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

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

你为什么把事情弄得这么复杂?您知道编译时属性的类型,只需使用一行代码即可轻松完成:

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

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

正如其他人所指出的,您可以使用委托,使用多种指定委托的方法之一。但是,如果您打算定期这样做,您应该考虑创建一个用于传递属性的包装类型,通过引用包装所需的委托,可以创建更好的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;
   }
}

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

创建从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);
    }
  }
}
它使代码隐藏类变得非常干净(如您所见),并且易于维护,因为所有繁重的工作都是由两个函数(GetQueryStringValue和ChangeType)完成的,这两个函数在每个页面类中重复使用,并且所有内容都是类型安全的(您将在GetQueryStringValue中注意到,您可以指定函数在无法转换值时是否抛出,或者仅使用返回默认值;这两种方法在不同的时间都适用,具体取决于您的应用程序)


此外,你甚至可以很容易地编写VS插件或CodeSmith脚本来生成派生的页面类。而且没有大量的委托和内容被传递,我发现新开发人员很难理解这些内容。

是的,问题的标题是相同的,但这是一个非常复杂的问题nfusing way…这看起来很棒。我已经实现了该方法,但是对它的调用给出了“无法从用法推断出方法“…”的类型参数。尝试显式指定类型参数。”这非常接近,但它迫使我这样做…setFromQueryString((int I)=>myFoo.Age=I,“inputAge”,context);我希望它推断类型。好的,这很有意义。我需要getter来推断类型。谢谢。这是一种方法,但我不想指定类型“”在调用中。这应该是从传入的内容推断出来的。我很确定它将在C#3Ope中推断出与SLaks解决方案相同的问题。它不会推断类型。当然,这是正常的方法,但我正在尝试使某些内容更可重用。我想看看是否有办法消除int.Parse以及封装的冗余我理解这一点,但你牺牲了相当多的可读性和可维护性,而不是真正意义上的任何东西;没有代码重用,因为它只是一行代码,而且你没有使它变得更优雅或更容易编码,依我看。一个真正优雅的解决方案,我使用的,是创建类(来源于System.Web.UI.Page)
var reference = new PropertyReference(
                        () => this.MyValue,
                        x => this.MyValue = x);

reference.Value = someNewValue;
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;
  }
}