içindeki işleyicilere bağımsız değişkenleri bağlama System.CommandLine

Önemli

System.CommandLine şu anda ÖNİzLEME aşamasındadır ve bu belgeler 2.0 beta 4 sürümüne yöneliktir. Bazı bilgiler, yayımlanmadan önce önemli ölçüde değiştirilebilen yayın öncesi ürünle ilgilidir. Burada verilen bilgilerle ilgili olarak Microsoft açık veya zımni hiçbir garanti vermez.

Bağımsız değişkenleri ayrıştırma ve komut işleyici koduna sağlama işlemine parametre bağlama adı verilir. System.CommandLine yerleşik birçok bağımsız değişken türünü bağlama özelliğine sahiptir. Örneğin, ve gibi FileInfoDirectoryInfo tamsayılar, sabit listeleri ve dosya sistemi nesneleri bağlanabilir. Çeşitli System.CommandLine türler de bağlanabilir.

Yerleşik bağımsız değişken doğrulama

Bağımsız değişkenlerin beklenen türleri ve arity'leri vardır. System.CommandLine bu beklentilerle eşleşmeyen bağımsız değişkenleri reddeder.

Örneğin, bir tamsayı seçeneğinin bağımsız değişkeni tamsayı değilse ayrıştırma hatası görüntülenir.

myapp --delay not-an-int
Cannot parse argument 'not-an-int' as System.Int32.

Birden çok bağımsız değişken en fazla bir değere sahip bir seçeneğe geçirilirse bir arity hatası görüntülenir:

myapp --delay-option 1 --delay-option 2
Option '--delay' expects a single argument but 2 were provided.

bu davranış olarak ayarlanarak Option.AllowMultipleArgumentsPerTokentruegeçersiz kılınabilir. Bu durumda, maksimum bir arity değerine sahip bir seçeneği yineleyebilirsiniz, ancak satırdaki yalnızca son değer kabul edilir. Aşağıdaki örnekte, değer three uygulamaya geçirilir.

myapp --item one --item two --item three

8 seçeneğe ve bağımsız değişkene kadar parametre bağlama

Aşağıdaki örnekte, komutunu çağırarak SetHandlerseçeneklerin komut işleyici parametrelerine nasıl bağlandığı gösterilmektedir:

var delayOption = new Option<int>
    ("--delay", "An option whose argument is parsed as an int.");
var messageOption = new Option<string>
    ("--message", "An option whose argument is parsed as a string.");

var rootCommand = new RootCommand("Parameter binding example");
rootCommand.Add(delayOption);
rootCommand.Add(messageOption);

rootCommand.SetHandler(
    (delayOptionValue, messageOptionValue) =>
    {
        DisplayIntAndString(delayOptionValue, messageOptionValue);
    },
    delayOption, messageOption);

await rootCommand.InvokeAsync(args);
public static void DisplayIntAndString(int delayOptionValue, string messageOptionValue)
{
    Console.WriteLine($"--delay = {delayOptionValue}");
    Console.WriteLine($"--message = {messageOptionValue}");
}

Lambda parametreleri, seçeneklerin ve bağımsız değişkenlerin değerlerini temsil eden değişkenlerdir:

(delayOptionValue, messageOptionValue) =>
{
    DisplayIntAndString(delayOptionValue, messageOptionValue);
},

Lambda'yı izleyen değişkenler, seçenek ve bağımsız değişken değerlerinin kaynakları olan seçenek ve bağımsız değişken nesnelerini temsil eder:

delayOption, messageOption);

Seçenekler ve bağımsız değişkenler, lambda'da ve lambda'yı izleyen parametrelerde aynı sırada bildirilmelidir. Sıra tutarlı değilse aşağıdaki senaryolardan biri sonuçlanır:

  • Sıra dışı seçenekler veya bağımsız değişkenler farklı türlerdeyse, bir çalışma zamanı özel durumu oluşturulur. Örneğin, kaynak int listesinde olması gereken bir string görünebilir.
  • Sıra dışı seçenekler veya bağımsız değişkenler aynı türdeyse, işleyici ona sağlanan parametrelerde yanlış değerleri sessizce alır. Örneğin, string seçenek x kaynak listesinde olması gereken yerde ystring görünebilir. Bu durumda seçenek değerinin değişkeni seçenek yx değerini alır.

Hem zaman uyumlu hem de zaman uyumsuz imzalarla en fazla 8 parametreyi destekleyen aşırı yüklemeler SetHandler vardır.

Parametre bağlama 8'den fazla seçenek ve bağımsız değişken

8'den fazla seçeneği işlemek veya birden çok seçenekten özel bir tür oluşturmak için veya özel bağlayıcı kullanabilirsiniz InvocationContext .

InvocationContext komutunu kullanma

Aşırı SetHandler yükleme nesneye InvocationContext erişim sağlar ve istediğiniz sayıda seçenek ve bağımsız değişken değeri almak için kullanabilirsiniz InvocationContext . Örnekler için bkz . Çıkış kodlarını ayarlama ve Sonlandırmayı işleme.

Özel bağlayıcı kullanma

Özel bağlayıcı, birden çok seçenek veya bağımsız değişken değerini karmaşık bir türde birleştirmenizi ve bunu tek bir işleyici parametresine geçirmenizi sağlar. Bir Person türe sahip olduğunuzu varsayalım:

public class Person
{
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
}

komutundan BinderBase<T>türetilmiş bir sınıf oluşturun; burada T komut satırı girişine göre oluşturulacak türdür:

public class PersonBinder : BinderBase<Person>
{
    private readonly Option<string> _firstNameOption;
    private readonly Option<string> _lastNameOption;

    public PersonBinder(Option<string> firstNameOption, Option<string> lastNameOption)
    {
        _firstNameOption = firstNameOption;
        _lastNameOption = lastNameOption;
    }

    protected override Person GetBoundValue(BindingContext bindingContext) =>
        new Person
        {
            FirstName = bindingContext.ParseResult.GetValueForOption(_firstNameOption),
            LastName = bindingContext.ParseResult.GetValueForOption(_lastNameOption)
        };
}

Özel bağlayıcı ile, özel türünüzün işleyicinize geçirilmesini, seçenekler ve bağımsız değişkenler için değerlerle aynı şekilde alabilirsiniz:

rootCommand.SetHandler((fileOptionValue, person) =>
    {
        DoRootCommand(fileOptionValue, person);
    },
    fileOption, new PersonBinder(firstNameOption, lastNameOption));

Yukarıdaki örneklerin alındığı programın tamamı aşağıda verilmiştir:

using System.CommandLine;
using System.CommandLine.Binding;

public class Program
{
    internal static async Task Main(string[] args)
    {
        var fileOption = new Option<FileInfo?>(
              name: "--file",
              description: "An option whose argument is parsed as a FileInfo",
              getDefaultValue: () => new FileInfo("scl.runtimeconfig.json"));

        var firstNameOption = new Option<string>(
              name: "--first-name",
              description: "Person.FirstName");

        var lastNameOption = new Option<string>(
              name: "--last-name",
              description: "Person.LastName");

        var rootCommand = new RootCommand();
        rootCommand.Add(fileOption);
        rootCommand.Add(firstNameOption);
        rootCommand.Add(lastNameOption);

        rootCommand.SetHandler((fileOptionValue, person) =>
            {
                DoRootCommand(fileOptionValue, person);
            },
            fileOption, new PersonBinder(firstNameOption, lastNameOption));

        await rootCommand.InvokeAsync(args);
    }

    public static void DoRootCommand(FileInfo? aFile, Person aPerson)
    {
        Console.WriteLine($"File = {aFile?.FullName}");
        Console.WriteLine($"Person = {aPerson?.FirstName} {aPerson?.LastName}");
    }

    public class Person
    {
        public string? FirstName { get; set; }
        public string? LastName { get; set; }
    }

    public class PersonBinder : BinderBase<Person>
    {
        private readonly Option<string> _firstNameOption;
        private readonly Option<string> _lastNameOption;

        public PersonBinder(Option<string> firstNameOption, Option<string> lastNameOption)
        {
            _firstNameOption = firstNameOption;
            _lastNameOption = lastNameOption;
        }

        protected override Person GetBoundValue(BindingContext bindingContext) =>
            new Person
            {
                FirstName = bindingContext.ParseResult.GetValueForOption(_firstNameOption),
                LastName = bindingContext.ParseResult.GetValueForOption(_lastNameOption)
            };
    }
}

Çıkış kodlarını ayarlama

Task- func aşırı yüklemeleri döndürülmektedirSetHandler. İşleyiciniz zaman uyumsuz koddan çağrılırsa, aşağıdaki örnekte olduğu gibi bunlardan birini kullanan bir işleyiciden döndürebilir Task<int> ve işlem çıkış kodunu ayarlamak için değerini kullanabilirsiniz int :

static async Task<int> Main(string[] args)
{
    var delayOption = new Option<int>("--delay");
    var messageOption = new Option<string>("--message");

    var rootCommand = new RootCommand("Parameter binding example");
    rootCommand.Add(delayOption);
    rootCommand.Add(messageOption);

    rootCommand.SetHandler((delayOptionValue, messageOptionValue) =>
        {
            Console.WriteLine($"--delay = {delayOptionValue}");
            Console.WriteLine($"--message = {messageOptionValue}");
            return Task.FromResult(100);
        },
        delayOption, messageOption);

    return await rootCommand.InvokeAsync(args);
}

Ancak lambdanın kendisinin zaman uyumsuz olması gerekiyorsa döndüremezsiniz Task<int>. Bu durumda kullanın InvocationContext.ExitCode. Tek parametre olarak öğesini belirten InvocationContext bir SetHandler aşırı yüklemesini kullanarak lambdanıza eklenen örneği alabilirsinizInvocationContext. Bu SetHandler aşırı yükleme nesneleri belirtmenize IValueDescriptor<T> izin vermez, ancak aşağıdaki örnekte gösterildiği gibi öğesinin ParseResult özelliğinden InvocationContextseçenek ve bağımsız değişken değerleri alabilirsiniz:

static async Task<int> Main(string[] args)
{
    var delayOption = new Option<int>("--delay");
    var messageOption = new Option<string>("--message");

    var rootCommand = new RootCommand("Parameter binding example");
    rootCommand.Add(delayOption);
    rootCommand.Add(messageOption);

    rootCommand.SetHandler(async (context) =>
        {
            int delayOptionValue = context.ParseResult.GetValueForOption(delayOption);
            string? messageOptionValue = context.ParseResult.GetValueForOption(messageOption);
        
            Console.WriteLine($"--delay = {delayOptionValue}");
            await Task.Delay(delayOptionValue);
            Console.WriteLine($"--message = {messageOptionValue}");
            context.ExitCode = 100;
        });

    return await rootCommand.InvokeAsync(args);
}

Yapacak zaman uyumsuz işiniz yoksa, aşırı yüklemeleri kullanabilirsiniz Action . Bu durumda, çıkış kodunu InvocationContext.ExitCode zaman uyumsuz lambda ile yaptığınız gibi ayarlayın.

Çıkış kodu varsayılan olarak 1'tir. Açıkça ayarlamazsanız, işleyiciniz normal şekilde çıktığında değeri 0 olarak ayarlanır. Özel durum oluşursa, varsayılan değeri korur.

Desteklenen türler

Aşağıdaki örneklerde yaygın olarak kullanılan bazı türleri bağlayan kod gösterilmektedir.

Numaralandırmalar

Türlerin enum değerleri ada bağlıdır ve bağlama büyük/küçük harfe duyarsızdır:

var colorOption = new Option<ConsoleColor>("--color");

var rootCommand = new RootCommand("Enum binding example");
rootCommand.Add(colorOption);

rootCommand.SetHandler((colorOptionValue) =>
    { Console.WriteLine(colorOptionValue); },
    colorOption);

await rootCommand.InvokeAsync(args);

Aşağıdaki örnek komut satırı girişi ve yukarıdaki örnekten elde edilen çıkış:

myapp --color red
myapp --color RED
Red
Red

Diziler ve listeler

Uygulayan IEnumerable birçok yaygın tür desteklenir. Örneğin:

var itemsOption = new Option<IEnumerable<string>>("--items")
    { AllowMultipleArgumentsPerToken = true };

var command = new RootCommand("IEnumerable binding example");
command.Add(itemsOption);

command.SetHandler((items) =>
    {
        Console.WriteLine(items.GetType());

        foreach (string item in items)
        {
            Console.WriteLine(item);
        }
    },
    itemsOption);

await command.InvokeAsync(args);

Aşağıdaki örnek komut satırı girişi ve yukarıdaki örnekten elde edilen çıkış:

--items one --items two --items three
System.Collections.Generic.List`1[System.String]
one
two
three

AllowMultipleArgumentsPerToken olarak ayarlandığındantrue, aşağıdaki giriş aynı çıkışla sonuçlanır:

--items one two three

Dosya sistemi türleri

Dosya sistemiyle çalışan komut satırı uygulamaları , FileInfove DirectoryInfo türlerini kullanabilirFileSystemInfo. Aşağıdaki örnekte kullanımı gösterilmektedir FileSystemInfo:

var fileOrDirectoryOption = new Option<FileSystemInfo>("--file-or-directory");

var command = new RootCommand();
command.Add(fileOrDirectoryOption);

command.SetHandler((fileSystemInfo) =>
    {
        switch (fileSystemInfo)
        {
            case FileInfo file                    :
                Console.WriteLine($"File name: {file.FullName}");
                break;
            case DirectoryInfo directory:
                Console.WriteLine($"Directory name: {directory.FullName}");
                break;
            default:
                Console.WriteLine("Not a valid file or directory name.");
                break;
        }
    },
    fileOrDirectoryOption);

await command.InvokeAsync(args);

ile FileInfo ve DirectoryInfo desen eşleştirme kodu gerekli değildir:

var fileOption = new Option<FileInfo>("--file");

var command = new RootCommand();
command.Add(fileOption);

command.SetHandler((file) =>
    {
        if (file is not null)
        {
            Console.WriteLine($"File name: {file?.FullName}");
        }
        else
        {
            Console.WriteLine("Not a valid file name.");
        }
    },
    fileOption);

await command.InvokeAsync(args);

Desteklenen diğer türler

Tek bir dize parametresi alan bir oluşturucuya sahip birçok tür bu şekilde bağlanabilir. Örneğin, ile FileInfo çalışacak kod bunun yerine ile Uri çalışır.

var endpointOption = new Option<Uri>("--endpoint");

var command = new RootCommand();
command.Add(endpointOption);

command.SetHandler((uri) =>
    {
        Console.WriteLine($"URL: {uri?.ToString()}");
    },
    endpointOption);

await command.InvokeAsync(args);

ve dosya sistemi türlerinin Uriyanı sıra aşağıdaki türler de desteklenir:

  • bool
  • byte
  • DateTime
  • DateTimeOffset
  • decimal
  • double
  • float
  • Guid
  • int
  • long
  • sbyte
  • short
  • uint
  • ulong
  • ushort

Nesneleri kullanma System.CommandLine

Nesneye InvocationContext erişmenizi sağlayan bir SetHandler aşırı yükleme vardır. Bu nesne daha sonra diğer System.CommandLine nesnelere erişmek için kullanılabilir. Örneğin, aşağıdaki nesnelere erişiminiz vardır:

InvocationContext

Örnekler için bkz . Çıkış kodlarını ayarlama ve Sonlandırmayı işleme.

CancellationToken

kullanma hakkında CancellationTokenbilgi için bkz . Sonlandırmayı işleme.

IConsole

IConsole test ve birçok genişletilebilirlik senaryolarını kullanmaktan System.Consoledaha kolay hale getirir. Özelliğinde InvocationContext.Console kullanılabilir.

ParseResult

ParseResult nesnesi özelliğinde InvocationContext.ParseResult kullanılabilir. Komut satırı girişini ayrıştırma sonuçlarını temsil eden tekil bir yapıdır. Komut satırında seçeneklerin veya bağımsız değişkenlerin olup olmadığını denetlemek veya özelliğini almak ParseResult.UnmatchedTokens için bunu kullanabilirsiniz. Bu özellik ayrıştırılmış ancak yapılandırılmış komut, seçenek veya bağımsız değişkenle eşleşmemiş belirteçlerin listesini içerir.

Eşleşmeyen belirteçlerin listesi, sarmalayıcı gibi davranan komutlarda kullanışlıdır. Sarmalayıcı komutu bir dizi belirteç alır ve bunları başka bir komut veya uygulamaya iletir. sudo Linux'taki komut bir örnektir. Kimliğine bürünmek için bir kullanıcının adını alır ve ardından çalıştırılacak bir komut alır. Örneğin:

sudo -u admin apt update

Bu komut satırı, apt update komutunu kullanıcısı adminolarak çalıştırır.

Bunun gibi bir sarmalayıcı komutu uygulamak için komut özelliğini TreatUnmatchedTokensAsErrors olarak falseayarlayın. ParseResult.UnmatchedTokens Ardından özelliği, komutuna açıkça ait olmayan tüm bağımsız değişkenleri içerir. Yukarıdaki örnekte ParseResult.UnmatchedTokens ve update belirteçleri yer alırapt. Komut işleyiciniz daha sonra öğesini yeni bir kabuk çağrısına iletebilir UnmatchedTokens , örneğin.

Özel doğrulama ve bağlama

Özel doğrulama kodu sağlamak için aşağıdaki örnekte gösterildiği gibi komutunuzda, seçeneğinizde veya bağımsız değişkeninizde çağrısı AddValidator yapın:

var delayOption = new Option<int>("--delay");
delayOption.AddValidator(result =>
{
    if (result.GetValueForOption(delayOption) < 1)
    {
        result.ErrorMessage = "Must be greater than 0";
    }
});

Girişi ayrıştırmak ve doğrulamak istiyorsanız, aşağıdaki örnekte gösterildiği gibi bir ParseArgument<T> temsilci kullanın:

var delayOption = new Option<int>(
      name: "--delay",
      description: "An option whose argument is parsed as an int.",
      isDefault: true,
      parseArgument: result =>
      {
          if (!result.Tokens.Any())
          {
              return 42;
          }

          if (int.TryParse(result.Tokens.Single().Value, out var delay))
          {
              if (delay < 1)
              {
                  result.ErrorMessage = "Must be greater than 0";
              }
              return delay;
          }
          else
          {
              result.ErrorMessage = "Not an int.";
              return 0; // Ignored.
          }
      });

Yukarıdaki kod olarak ayarlır isDefaulttrue , böylece parseArgument kullanıcı bu seçenek için bir değer girmese bile temsilci çağrılır.

ile gerçekleştiremezseniz ParseArgument<T> yapabileceklerinizle ilgili bazı örnekler aşağıda verilmiştir AddValidator:

  • Aşağıdaki örnekteki sınıfı gibi özel türleri ayrıştırma Person :

    public class Person
    {
        public string? FirstName { get; set; }
        public string? LastName { get; set; }
    }
    
    var personOption = new Option<Person?>(
          name: "--person",
          description: "An option whose argument is parsed as a Person",
          parseArgument: result =>
          {
              if (result.Tokens.Count != 2)
              {
                  result.ErrorMessage = "--person requires two arguments";
                  return null;
              }
              return new Person
              {
                  FirstName = result.Tokens.First().Value,
                  LastName = result.Tokens.Last().Value
              };
          })
    {
        Arity = ArgumentArity.OneOrMore,
        AllowMultipleArgumentsPerToken = true
    };
    
  • Diğer giriş dizelerinin ayrıştırılması (örneğin, "1,2,3" öğesini içine ayrıştırın int[]).

  • Dinamik arity. Örneğin, dize dizileri olarak tanımlanan iki bağımsız değişkeniniz vardır ve komut satırı girişinde bir dizi dizeyi işlemeniz gerekir. yöntemi, ArgumentResult.OnlyTake giriş dizelerini bağımsız değişkenler arasında dinamik olarak bölmenizi sağlar.

Ayrıca bkz.

System.CommandLine Genel bakış