Переменное количество результатов от функции

Во многих местах у меня есть код, похожий на следующий:

var dbParams = db.ReadParams(memberID, product, GetSubscriptionFields());
Debug.Assert(dbParams.Count == 4);

_memberCode = dbParams[0];
_password = dbParams[1];
_userName = dbParams[2];
_reasonCode = dbParams[3];

ReadParams () возвращает массив строк, количество строк зависит от функции GetSubscriptionFields (). Я мог бы использовать dbParams [] непосредственно в моем коде, но я считаю более полезным давать осмысленные имена каждому значению в массиве. Есть ли способ получить все результаты напрямую, не просматривая массив?

Я ищу что-то вроде:

db.ReadParams(memberID, product, out _memberCode, out _password, out _userName, out _reasonCode);

или же

Tuple<_memberCode, _password, _userName, _reasonCode> = db.ReadParams(memberID, product);

Конечно, это должен быть законный код C# :)

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
194
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

Почему бы вместо этого не использовать константы?

Тогда в вашем коде вы могли бы иметь

dbParams[MEMBER_CODE]
dbParams[PASSWORD]
dbParams[USERNAME]
dbParams[REASON_CODE]

который отвечает вашей цели создания значимых имен без изменения способа работы метода.

Мне нравится эта идея, ее проще реализовать, чем у Меки.

Marcel Popescu 05.11.2008 14:33

Я думаю, что ваша идея с Tuple довольно хороша. Вы можете определить это так:

public class Tuple<T1, T2, T3, T4>
{
    public T1 Field1 { get; set; }
    public T2 Field2 { get; set; }
    public T3 Field3 { get; set; }
    public T4 Field4 { get; set; }
}

Вы, вероятно, захотите определить несколько из них с двумя или тремя свойствами. К сожалению, это не поможет вам назвать свойства класса. На самом деле нет способа сделать это (по крайней мере, до C# 4.0, когда вы могли использовать динамическую типизацию с анонимным типом.

Тем не менее, он по-прежнему позиционален - т.е. доступ к результату. Поле1 не сильно отличается от доступа к результату [0] (при условии, что они имеют одинаковый тип)

Marc Gravell 05.11.2008 14:16

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

David Wengier 05.11.2008 14:18

Не совсем; поскольку количество аргументов не фиксировано, лучшего способа сделать это на самом деле нет. Проблема с обычными кортежами заключается в том, что вы все еще работаете позиционно - просто с «.Value0» вместо «[0]». А анонимные типы не могут быть напрямую представлены в API.

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

 return new Foo {MemberCode = arr[0], ...}

(где Foo - ваш класс, который представляет собой результат, с именованными типизированными свойствами)

В качестве альтернативы вы можете добавить их в словарь, но это не помогает вызывающей стороне больше, чем массив.

Единственный другой вариант - это что-то В самом деле grungy, например, принятие массива params Action<string>, который вы используете для назначения каждого. Я подробно остановлюсь на последнем просто для развлечения - я не предлагаю вам это делать:

static void Main()
{
    string name = "";
    int value = 0;
    Foo("whatever",
        x => { name = x; },
        x => { value = int.Parse(x); });    
}
// yucky; wash eyes after reading...
static void Foo(string query, params Action<string>[] actions)
{
    string[] results = Bar(query); // the actual query
    int len = actions.Length < results.Length ? actions.Length : results.Length;
    for (int i = 0; i < len; i++)
    {
        actions[i](results[i]);
    }
}

Вы пишете код на очень объектно-ориентированном языке, так почему бы вам не использовать объекты?

Member m = db.ReadParams(memberID, product, GetSubscriptionFields());

и в вашем коде вы используете

m.memberCode
m.password
m.username
m.reasonCode

Конечно, вам не нужно делать значения общедоступными, вы можете сделать их доступными только с помощью методов setter / getter, и, имея только геттеры, вы можете избежать их изменения после создания объекта.

Конечно, разные вызовы db.ReadParams должны возвращать разные объекты, например. вы можете создать абстрактный базовый класс и унаследовать все возможные результаты от него db.ReadParams. Поэтому вам может потребоваться инкапсулировать db.ReadParams в другой метод, который определяет правильный тип объекта для создания:

ReadParamsResult rpr = myDb.ReadParamsAsObject(memberID, product, GetSubscriptionFields());

// Verify that the expected result object has been returned
Debug.Assert(rpr is Member);

// Downcast
Member m = (Member)rpr;

Отличная идея, спасибо. Сейчас я выберу другой, потому что его проще и быстрее реализовать, но я ценю это и могу использовать его позже.

Marcel Popescu 05.11.2008 14:34

Я тоже придумал это ... Мне это нравится больше всего, но это личное предпочтение, я понимаю, что это определенно не самая чистая идея:

using System.Diagnostics;

public static class ArrayExtractor
{
  public static void Extract<T1>(this object[] array, out T1 value1)
      where T1 : class
  {
    Debug.Assert(array.Length >= 1);
    value1 = array[0] as T1;
  }

  public static void Extract<T1, T2>(this object[] array, out T1 value1, out T2 value2)
      where T1 : class
      where T2 : class
  {
    Debug.Assert(array.Length >= 2);
    value1 = array[0] as T1;
    value2 = array[1] as T2;
  }
}

Конечно, я расширяю этот класс до 10 или 15 аргументов.

Использование:

string fileName;
string contents;
ArrayExtractor.Extract(args, out fileName, out contents);

или даже лучше

args.Extract(out fileName, out contents);

где args - это, конечно, массив объектов.

Другие вопросы по теме