C # Аргументы переменной длины, что лучше и почему: __arglist, массив params или Dictionary <T, K>? - PullRequest
8 голосов
/ 26 мая 2009

Я недавно прочитал следующий пост переполнения: Скрытые возможности C #

Одной из упомянутых особенностей был arglist. Почему нужно выбирать это или альтернативы как способ использования списка аргументов переменной длины для метода? Кроме того, обратите внимание, что я, вероятно, не буду использовать такую ​​конструкцию в своем коде, если это не оправдано угловым случаем. Это больше вопрос семантики, чем практичности или целесообразности использования аргументов переменной длины. Так кто-нибудь знает, что лучше и почему?

 [Test]
 public void CanHandleVariableLengthArgs()
 {
     TakeVariableLengthArgs(__arglist(new StringBuilder(), 12));

     object[] arr = { new StringBuilder() };
     TakeVariableLengthArgs2(arr);

     TakeVariableLengthArgs3(
         new Dictionary<string, object> 
         { { "key", new StringBuilder() } });
 }

 public void TakeVariableLengthArgs(__arglist)
 {
      var args = new ArgIterator(__arglist);

      var a = (StringBuilder)TypedReference.ToObject(args.GetNextArg());
      a.Append(1);
 }

 public void TakeVariableLengthArgs2(params object[] args)
 {
      var a = (StringBuilder)args[0];
      a.Append(1);
 }

 public void TakeVariableLengthArgs3(Dictionary<string, object> args)
 {
      var a = (StringBuilder)args["StringBuilder"];
      a.Append(1);
 }

Ответы [ 5 ]

8 голосов
/ 26 мая 2009

Я бы, конечно, никогда не использовал __arglist, поскольку он недокументирован, и никто не знает, что это значит в любом случае.

Я бы также как можно дольше избегал списков аргументов переменной длины и вместо этого переработал бы мой дизайн, чтобы понять, что действительно является переменной, и моделировать эту изменчивость менее зависимым от платформы образом.

4 голосов
/ 26 мая 2009

C # 4 будет иметь лучший механизм для этого; именованные и необязательные аргументы :

static void Main(string[] args)
{
    // The method can be called in the normal way, by using positional arguments.
    Console.WriteLine(CalculateBMI(123, 64));

    // Named arguments can be supplied for the parameters in either order.
    Console.WriteLine(CalculateBMI(weight: 123, height: 64));
    Console.WriteLine(CalculateBMI(height: 64, weight: 123));

    // Positional arguments cannot follow named arguments.
    // The following statement causes a compiler error.
    //Console.WriteLine(CalculateBMI(weight: 123, 64));

    // Named arguments can follow positional arguments.
    Console.WriteLine(CalculateBMI(123, height: 64));
}

static int CalculateBMI(int weight, int height)
{
    return (weight * 703) / (height * height);
}
3 голосов
/ 26 мая 2009

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

  • Они более вероятно изменятся, чем установленные, документированные функции
  • Они могут иметь побочные эффекты и последствия, которые не очевидны при их использовании
  • Другие разработчики не будут знакомы с ними, и им будет сложнее поддерживать ваш код
  • Инструменты рефакторинга (например, сама VS или Resharper ) вряд ли смогут их распознать
  • Они убирают ясность из намерений вашего кода
  • Существуют поддерживаемые языком альтернативы большинству из них

В конкретном случае __arglist вы можете добиться тех же возможностей с помощью поддерживаемого ключевого слова params , которое позволяет создавать списки аргументов переменных типа для безопасных типов для методов в C #. Тем не менее, на практике я был бы осторожен, используя его, поскольку он может запутать ваш код, если он используется неправильно - хорошие варианты использования (например, в string.Format (), который принимает переменные аргументы) - встречаются реже, чем вы думаете. *

3 голосов
/ 26 мая 2009

Зависит от случая. Я использовал params в тех случаях, когда у меня есть переменное число аргументов, и это значительно повышает удобочитаемость вызывающего кода.

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

public static void Combine(Stream output, params Stream[] sources) { /* ... */ }
public static void Combine(Stream output, params string[] sourceFiles) { /* ... */ }

, которые при использовании делают клиентский код действительно приятным:

using (FileStream output = new FileStream(outputPath, FileMode.Create)) {
    TiffDocument.Combine(output, tpsCoverSheetPath, mainDocumentPath, tpsTrailerPath);
}
0 голосов
/ 26 мая 2009

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

Просто так много запаха кода в этом способе кодирования для меня. переменная длина коллекция объект ? Не прошел бы незамеченным в моем обзоре кода.

Редактировать : И если бы он прошел мой анализ кода, то по всей вероятности, этот параметр был бы экземпляром IEnumerable и ни одним из трех элементов предложения. IEnumerable - это самая скудная вещь, которая может заключать в себе мои потребности.

...