Передача C# свойство ссылкой

Там должен так или иначе передать свойство Объекта ссылкой? Я знаю, что могу передать целый объект, но я хочу определить свойство объекта установить и проверить, что это - тип, таким образом, я знаю, как проанализировать. Я должен, возможно, проявить другой подход (я не могу изменить исходный объект в так или иначе)?

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);
}
23
задан ctrlShiftBryan 3 March 2010 в 22:07
поделиться

8 ответов

Функцию можно вызвать с помощью лямбда-выражения:

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);
18
ответ дан 29 November 2019 в 02:04
поделиться

Нет, нет возможности напрямую передать свойство по ссылке. Visual Basic предлагает эту поддержку на языке, помещая значение свойства во временную переменную, а затем оно передается по ссылке и повторно назначается при возврате.

В C # это поведение можно только приблизительно описать, передав Func для получения значения свойства и Action для установки значения (с использованием замыканий ), где T - тип свойства.

5
ответ дан 29 November 2019 в 02:04
поделиться

Вы можете обернуть свойство соответствующими методами и делегатами и передать делегаты.

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);
}
5
ответ дан 29 November 2019 в 02:04
поделиться

Как указывали другие, вы можете сделать это с помощью делегатов, используя один из многих способов указания делегатов. Однако, если вы намереваетесь делать это регулярно, вам следует подумать о создании типа оболочки для передачи свойств по ссылке, которая обертывает необходимые делегаты, это может создать более удобный 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;
6
ответ дан 29 November 2019 в 02:04
поделиться

Передача функции с помощью лямбды, вероятно, наиболее элегантна, но если вам нужно простое решение проблемы

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)
{
...
}
2
ответ дан 29 November 2019 в 02:04
поделиться

Почему бы не использовать дженерики и не вернуть объект?

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);
}
2
ответ дан 29 November 2019 в 02:04
поделиться

Вот вам совершенно другое решение:

Создайте классы, производные от 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, чтобы легко сгенерировать производный класс страницы. И здесь не передается куча делегатов и прочего, что, как мне кажется, новым разработчикам трудно понять.

1
ответ дан 29 November 2019 в 02:04
поделиться

Почему вы так усложняете? Вы знаете тип свойства во время компиляции, просто сделайте это простым способом с помощью одной строчки кода:

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

Если вам нужно проверить тип, просто добавьте небольшую функцию, которая обертывает int.TryParse () и возвращает безобидный результат (например, 0), если вы получите «pdq» в значении строки запроса вместо числа.

1
ответ дан 29 November 2019 в 02:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: