Atmalar - C# Ile İlgili Temel Bilgiler

C# 7.0'dan başlayarak, C# uygulama kodunda kasıtlı olarak kullanılmayan yer tutucu değişkenler olan atmaları destekler. Atmalar atanmamış değişkenlere eşdeğerdir; bir değeri yoktur. Atma, amacı derleyiciye ve kodunuzu okuyan diğer kullanıcılara iletir: İfadenin sonucunu yoksaymayı amaçlamıştınız. Bir ifadenin sonucunu, bir tanımlama grubu ifadesinin bir veya daha fazla üyesini, bir out yöntemin parametresini veya desen eşleştirme ifadesinin hedefini yoksaymak isteyebilirsiniz.

Yalnızca tek bir atma değişkeni olduğundan, bu değişkene depolama alanı bile atanamayabilir. Atmalar bellek ayırmalarını azaltabilir. Atmalar, kodunuzun amacını net hale getirir. Okunabilirliğini ve sürdürülebilirliğini artırır.

Bir değişkenin, adı olarak alt çizgi (_) atayarak atılmış bir değişken olduğunu belirtirsiniz. Örneğin, aşağıdaki yöntem çağrısı, birinci ve ikinci değerlerin atıldığı bir tanımlama grubu döndürür. area , tarafından GetCityInformationdöndürülen üçüncü bileşene ayarlanmış daha önce bildirilen bir değişkendir:

(_, _, area) = city.GetCityInformation(cityName);

C# 9.0 sürümünden başlayarak, bir lambda ifadesinin kullanılmayan giriş parametrelerini belirtmek için atmaları kullanabilirsiniz. Daha fazla bilgi için Lambda ifadeleri makalesinin Lambda ifadesinin giriş parametreleri bölümüne bakın.

Geçerli bir atma olduğunda _ , değerini almaya veya atama işleminde kullanmaya çalışmak, "'_' adı geçerli bağlamda yok" derleyici hatası CS0301 oluşturur. Bu hatanın nedeni _ , bir değer atanmaması ve hatta depolama konumu atanmamasıdır. Gerçek bir değişkense, önceki örnekte olduğu gibi birden fazla değeri atamazsınız.

Tanımlama grubu ve nesne yapısı kaldırma

Atmalar, uygulama kodunuz bazı tanımlama grubu öğelerini kullandığında ancak diğerlerini yoksaydığında tanımlama gruplarıyla çalışırken yararlıdır. Örneğin, aşağıdaki QueryCityDataForYears yöntem bir şehir adı, alanı, bir yıl, o yıl için şehir nüfusu, ikinci yıl ve o ikinci yıl için şehrin nüfusu ile bir tanımlama grubu döndürür. Örnekte, popülasyonda bu iki yıl arasındaki değişiklik gösterilmektedir. Kayıt grubundaki verilerden şehir alanıyla bağdaştırılmamıştır ve tasarım zamanında şehir adını ve iki tarihi biliyoruz. Sonuç olarak, yalnızca tanımlama grubunda depolanan iki popülasyon değeriyle ilgileniyoruz ve kalan değerlerini atılmış olarak işleyebiliriz.

var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", 1960, 2010);

Console.WriteLine($"Population change, 1960 to 2010: {pop2 - pop1:N0}");

static (string, double, int, int, int, int) QueryCityDataForYears(string name, int year1, int year2)
{
    int population1 = 0, population2 = 0;
    double area = 0;

    if (name == "New York City")
    {
        area = 468.48;
        if (year1 == 1960)
        {
            population1 = 7781984;
        }
        if (year2 == 2010)
        {
            population2 = 8175133;
        }
        return (name, area, year1, population1, year2, population2);
    }

    return ("", 0, 0, 0, 0, 0);
}
// The example displays the following output:
//      Population change, 1960 to 2010: 393,149

Demetleri atmalarla birlikte yapılandırma hakkında daha fazla bilgi için bkz. Tanımlama demetlerini ve diğer türleri yapılandırma.

Deconstruct Bir sınıf, yapı veya arabirimin yöntemi, bir nesneden belirli bir veri kümesini almanıza ve kaldırmanıza da olanak tanır. Kaldırılan değerlerin yalnızca bir alt kümesiyle çalışmak istediğinizde atmaları kullanabilirsiniz. Aşağıdaki örnek, bir Person nesneyi dört dizeye (ad ve soyadlar, şehir ve eyalet) ayırır, ancak soyadını ve eyaleti atar.

using System;

namespace Discards
{
    public class Person
    {
        public string FirstName { get; set; }
        public string MiddleName { get; set; }
        public string LastName { get; set; }
        public string City { get; set; }
        public string State { get; set; }

        public Person(string fname, string mname, string lname,
                      string cityName, string stateName)
        {
            FirstName = fname;
            MiddleName = mname;
            LastName = lname;
            City = cityName;
            State = stateName;
        }

        // Return the first and last name.
        public void Deconstruct(out string fname, out string lname)
        {
            fname = FirstName;
            lname = LastName;
        }

        public void Deconstruct(out string fname, out string mname, out string lname)
        {
            fname = FirstName;
            mname = MiddleName;
            lname = LastName;
        }

        public void Deconstruct(out string fname, out string lname,
                                out string city, out string state)
        {
            fname = FirstName;
            lname = LastName;
            city = City;
            state = State;
        }
    }
    class Example
    {
        public static void Main()
        {
            var p = new Person("John", "Quincy", "Adams", "Boston", "MA");

            // Deconstruct the person object.
            var (fName, _, city, _) = p;
            Console.WriteLine($"Hello {fName} of {city}!");
            // The example displays the following output:
            //      Hello John of Boston!
        }
    }
}

Atılanlarla kullanıcı tanımlı türlerin yapılandırılması hakkında daha fazla bilgi için bkz. Tanımlama demetlerini ve diğer türleri yapılandırma.

ile desen eşleştirme switch

Atma deseni, switch ifadesiyle desen eşleştirmede kullanılabilir. dahil olmak üzere nullher ifade her zaman atma deseni ile eşleşir.

Aşağıdaki örnek, bir ProvidesFormatInfo nesnenin bir switch uygulama sağlayıp sağlamadığını belirlemek ve nesnenin nullolup olmadığını test etmek için ifade kullanan bir IFormatProvider yöntemi tanımlar. Ayrıca, başka bir türdeki null olmayan nesneleri işlemek için atma desenini kullanır.

object?[] objects = { CultureInfo.CurrentCulture,
                   CultureInfo.CurrentCulture.DateTimeFormat,
                   CultureInfo.CurrentCulture.NumberFormat,
                   new ArgumentException(), null };
foreach (var obj in objects)
    ProvidesFormatInfo(obj);

static void ProvidesFormatInfo(object? obj) =>
    Console.WriteLine(obj switch
    {
        IFormatProvider fmt => $"{fmt.GetType()} object",
        null => "A null object reference: Its use could result in a NullReferenceException",
        _ => "Some object type without format information"
    });
// The example displays the following output:
//    System.Globalization.CultureInfo object
//    System.Globalization.DateTimeFormatInfo object
//    System.Globalization.NumberFormatInfo object
//    Some object type without format information
//    A null object reference: Its use could result in a NullReferenceException

Parametrelerle out yöntemlere yapılan çağrılar

Kullanıcı tanımlı bir türü (bir sınıf, yapı veya arabirimin örneği) yok etmek için yöntemini çağırırken Deconstruct , bağımsız out değişkenlerin değerlerini atabilirsiniz. Ancak bir parametre ile herhangi bir out yöntemi çağırırken bağımsız değişkenlerin out değerini de atabilirsiniz.

Aşağıdaki örnek, bir tarihin dize gösteriminin geçerli kültürde geçerli olup olmadığını belirlemek için DateTime.TryParse(String, out DateTime) yöntemini çağırır. Örnek yalnızca tarih dizesini doğrulamayla ilgili olduğundan ve tarihi ayıklamak için ayrıştırmayla ilgilenmediğinden, out yöntemin bağımsız değişkeni atılır.

string[] dateStrings = {"05/01/2018 14:57:32.8", "2018-05-01 14:57:32.8",
                      "2018-05-01T14:57:32.8375298-04:00", "5/01/2018",
                      "5/01/2018 14:57:32.80 -07:00",
                      "1 May 2018 2:57:32.8 PM", "16-05-2018 1:00:32 PM",
                      "Fri, 15 May 2018 20:10:57 GMT" };
foreach (string dateString in dateStrings)
{
    if (DateTime.TryParse(dateString, out _))
        Console.WriteLine($"'{dateString}': valid");
    else
        Console.WriteLine($"'{dateString}': invalid");
}
// The example displays output like the following:
//       '05/01/2018 14:57:32.8': valid
//       '2018-05-01 14:57:32.8': valid
//       '2018-05-01T14:57:32.8375298-04:00': valid
//       '5/01/2018': valid
//       '5/01/2018 14:57:32.80 -07:00': valid
//       '1 May 2018 2:57:32.8 PM': valid
//       '16-05-2018 1:00:32 PM': invalid
//       'Fri, 15 May 2018 20:10:57 GMT': invalid

Tek başına atma

Yoksaymayı seçtiğiniz herhangi bir değişkeni belirtmek için tek başına atma kullanabilirsiniz. Tipik kullanımlardan biri, bir bağımsız değişkenin null olmadığından emin olmak için atama kullanmaktır. Aşağıdaki kod, atamayı zorlamak için bir atma kullanır. Atamanın sağ tarafında, bağımsız değişken olduğunda bir System.ArgumentNullException atmak için null birleşim işleci kullanılırnull. Kodun atamanın sonucuna ihtiyacı olmadığından atılır. İfade null denetime zorlar. Atma, amacınızı netleştirir: atamanın sonucu gerekli değildir veya kullanılmaz.

public static void Method(string arg)
{
    _ = arg ?? throw new ArgumentNullException(paramName: nameof(arg), message: "arg can't be null");

    // Do work with arg.
}

Aşağıdaki örnek, zaman uyumsuz bir işlem tarafından döndürülen nesneyi yoksaymak Task için tek başına atma kullanır. Görevin atanması, işlemin tamamlanmak üzere olduğu için attığı özel durumu gizleme etkisine sahiptir. Amacınızı netleştirir: öğesini atmak Taskve bu zaman uyumsuz işlemden oluşturulan hataları yoksaymak istiyorsunuz.

private static async Task ExecuteAsyncMethods()
{
    Console.WriteLine("About to launch a task...");
    _ = Task.Run(() =>
    {
        var iterations = 0;
        for (int ctr = 0; ctr < int.MaxValue; ctr++)
            iterations++;
        Console.WriteLine("Completed looping operation...");
        throw new InvalidOperationException();
    });
    await Task.Delay(5000);
    Console.WriteLine("Exiting after 5 second delay");
}
// The example displays output like the following:
//       About to launch a task...
//       Completed looping operation...
//       Exiting after 5 second delay

Görevi ataya atamadan, aşağıdaki kod bir derleyici uyarısı oluşturur:

private static async Task ExecuteAsyncMethods()
{
    Console.WriteLine("About to launch a task...");
    // CS4014: Because this call is not awaited, execution of the current method continues before the call is completed.
    // Consider applying the 'await' operator to the result of the call.
    Task.Run(() =>
    {
        var iterations = 0;
        for (int ctr = 0; ctr < int.MaxValue; ctr++)
            iterations++;
        Console.WriteLine("Completed looping operation...");
        throw new InvalidOperationException();
    });
    await Task.Delay(5000);
    Console.WriteLine("Exiting after 5 second delay");

Not

Bir hata ayıklayıcı kullanarak önceki iki örnekten birini çalıştırırsanız, özel durum oluşturulduğunda hata ayıklayıcı programı durdurur. Bir hata ayıklayıcısı eklenmeden, her iki durumda da özel durum sessizce yoksayılır.

_ ayrıca geçerli bir tanımlayıcıdır. Desteklenen bağlam dışında kullanıldığında, atma olarak değil geçerli _ bir değişken olarak değerlendirilir. adlı _ bir tanımlayıcı zaten kapsamdaysa, tek başına atma olarak kullanılması _ aşağıdakilere neden olabilir:

  • Kapsam _ içi değişkenin değerini, istenen atma değerini atayarak yanlışlıkla değiştirilmesi. Örneğin:
    private static void ShowValue(int _)
    {
       byte[] arr = { 0, 0, 1, 2 };
       _ = BitConverter.ToInt32(arr, 0);
       Console.WriteLine(_);
    }
     // The example displays the following output:
     //       33619968
    
  • Tür güvenliğini ihlal etmek için derleyici hatası. Örneğin:
    private static bool RoundTrips(int _)
    {
       string value = _.ToString();
       int newValue = 0;
       _ = Int32.TryParse(value, out newValue);
       return _ == newValue;
    }
    // The example displays the following compiler error:
    //      error CS0029: Cannot implicitly convert type 'bool' to 'int'
    
  • Derleyici hatası CS0136, "'_' adlı yerel veya parametre, yerel veya parametre tanımlamak için kapsayan bir yerel kapsamda kullanıldığından bu kapsamda bildirilemiyor." Örneğin:
     public void DoSomething(int _)
    {
     var _ = GetValue(); // Error: cannot declare local _ when one is already in scope
    }
    // The example displays the following compiler error:
    // error CS0136:
    //       A local or parameter named '_' cannot be declared in this scope
    //       because that name is used in an enclosing local scope
    //       to define a local or parameter
    

Ayrıca bkz.