C#通过引用传递属性

无论如何通过引用传递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); } 

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

 private void setFromQueryString(Action 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(i => myFoo.Age = i, "inputAge", context); 

编辑 :如果你真的想要类型推断:

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

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

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

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

 delegate int IntGetter(T obj); delegate void IntSetter(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(SetAge), foo, "inputAge", context); } private void setFromQueryString( IntSetter intSetter, T obj, String queryString, HttpContext context) { String valueString = context.Request.QueryString[queryString].ToString(); intSetter(T, valueString); } 

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

例如:

 class PropertyReference { public T Value { get { return this.getter(); } set { this.setter(value); } } public PropertyReference(Func getter, Action setter) { this.getter = getter; this.setter = setter; } } 

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

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

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

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

 private T setFromQueryString(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("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); } 

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

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

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

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

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

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

 public class MyBasePage : System.Web.UI.Page { public T GetQueryStringValue( string value, T defaultValue, bool throwOnBadConvert) { T returnValue; if (string.IsNullOrEmpty(value)) return defaultValue; else returnValue = ConvertType(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( 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(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脚本来轻松生成派生页面类。 并且没有一大堆代表和东西被传递,我发现新开发人员很难理解。