C# 通过引用传递属性

发布于 2024-08-23 16:05:53 字数 802 浏览 7 评论 0原文

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

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

寄居人 2024-08-30 16:05:53

您可以使用 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);

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);
清浅ˋ旧时光 2024-08-30 16:05:53

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

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;
眼泪淡了忧伤 2024-08-30 16:05:53

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

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

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.

栖迟 2024-08-30 16:05:53

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

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

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);
}
天赋异禀 2024-08-30 16:05:53

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

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)
{
...
}
尤怨 2024-08-30 16:05:53

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

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

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);
}
清泪尽 2024-08-30 16:05:53

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

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

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

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.

浅唱ヾ落雨殇 2024-08-30 16:05:53

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

创建从 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 脚本来轻松生成派生页面类。而且没有一堆代表和东西被传递,我发现新开发人员很难理解这一点。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文