Во многих местах у меня есть код, похожий на следующий:
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# :)





Почему бы вместо этого не использовать константы?
Тогда в вашем коде вы могли бы иметь
dbParams[MEMBER_CODE]
dbParams[PASSWORD]
dbParams[USERNAME]
dbParams[REASON_CODE]
который отвечает вашей цели создания значимых имен без изменения способа работы метода.
Я думаю, что ваша идея с 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] (при условии, что они имеют одинаковый тип)
Вы правы, но есть и другие преимущества. Например, если вы напишете соответствующий конструктор и ограничите свойства только для чтения, то вы, по крайней мере, получите гарантию того, что получите обратно нужное количество значений.
Не совсем; поскольку количество аргументов не фиксировано, лучшего способа сделать это на самом деле нет. Проблема с обычными кортежами заключается в том, что вы все еще работаете позиционно - просто с «.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;
Отличная идея, спасибо. Сейчас я выберу другой, потому что его проще и быстрее реализовать, но я ценю это и могу использовать его позже.
Я тоже придумал это ... Мне это нравится больше всего, но это личное предпочтение, я понимаю, что это определенно не самая чистая идея:
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 - это, конечно, массив объектов.
Мне нравится эта идея, ее проще реализовать, чем у Меки.