Tek dönüş sözdizimi ifadesi


106

Bunun garip gelebileceğini biliyorum ama bu sözdizimini internette nasıl arayacağımı bile bilmiyorum ve tam olarak ne anlama geldiğinden de emin değilim.

Bu yüzden bazı MoreLINQ kodunu izledim ve sonra bu yöntemi fark ettim

public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
{
    if (source == null) throw new ArgumentNullException(nameof(source));
    if (keySelector == null) throw new ArgumentNullException(nameof(keySelector));

    return _(); IEnumerable<TSource> _()
    {
        var knownKeys = new HashSet<TKey>(comparer);
        foreach (var element in source)
        {
            if (knownKeys.Add(keySelector(element)))
                yield return element;
        }
    }
}

Bu garip dönüş ifadesi nedir? return _();?


6
Yoksa demek: return _(); IEnumerable<TSource> _()?
Alex K.

6
@Steve, OP'nin ' return _(); IEnumerable<TSource> _()dan daha çok yield returnmu bahsettiğini merak ediyorum.
Rob

5
Sanırım bu satırı kastetti return _(); IEnumerable<TSource> _(). Gerçek dönüş ifadesinden ziyade göründüğü şekliyle kafası karışabilir.
Mateusz

5
@AkashKava OP, garip bir dönüş açıklaması olduğunu söyledi. Maalesef, kod iki dönüş ifadesi içeriyor . Bu nedenle, insanların neyi kastettiği konusunda kafalarının karışması anlaşılır bir durumdur.
mjwills

5
Soruyu düzenledim ve karışıklık için bir kez daha özür dilerim.
kuskmen

Yanıtlar:


106

Bu, yerel işlevleri destekleyen C # 7.0'dır ...

public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
       this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
    {
        if (source == null) throw new 
           ArgumentNullException(nameof(source));
        if (keySelector == null) throw 
             new ArgumentNullException(nameof(keySelector));

        // This is basically executing _LocalFunction()
        return _LocalFunction(); 

        // This is a new inline method, 
        // return within this is only within scope of
        // this method
        IEnumerable<TSource> _LocalFunction()
        {
            var knownKeys = new HashSet<TKey>(comparer);
            foreach (var element in source)
            {
                if (knownKeys.Add(keySelector(element)))
                    yield return element;
            }
        }
    }

Geçerli C # ile Func<T>

public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
       this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
    {
        if (source == null) throw new 
           ArgumentNullException(nameof(source));
        if (keySelector == null) throw 
             new ArgumentNullException(nameof(keySelector));

        Func<IEnumerable<TSource>> func = () => {
            var knownKeys = new HashSet<TKey>(comparer);
            foreach (var element in source)
            {
                if (knownKeys.Add(keySelector(element)))
                    yield return element;
            }
       };

        // This is basically executing func
        return func(); 

    }

İşin püf noktası, _ () kullanıldıktan sonra ilan edilir, ki bu tamamen iyidir.

Yerel işlevlerin pratik kullanımı

Yukarıdaki örnek, satır içi yöntemin nasıl kullanılabileceğinin bir göstergesidir, ancak büyük olasılıkla yöntemi yalnızca bir kez çağıracaksanız, o zaman hiçbir faydası yoktur.

Ancak yukarıdaki örnekte, Phoshi ve Luaan'ın yorumlarında belirtildiği gibi, yerel işlevi kullanmanın bir avantajı vardır. Getiri dönüşlü işlev, birisi yinelemedikçe çalıştırılmayacağından, bu durumda yerel işlevin dışındaki yöntem çalıştırılacak ve hiç kimse değeri yinelemese bile parametre doğrulaması yapılacaktır.

Çoğu zaman yöntemde kodu tekrarladık, bu örneğe bakalım ..

  public void ValidateCustomer(Customer customer){

      if( string.IsNullOrEmpty( customer.FirstName )){
           string error = "Firstname cannot be empty";
           customer.ValidationErrors.Add(error);
           ErrorLogger.Log(error);
           throw new ValidationError(error);
      }

      if( string.IsNullOrEmpty( customer.LastName )){
           string error = "Lastname cannot be empty";
           customer.ValidationErrors.Add(error);
           ErrorLogger.Log(error);
           throw new ValidationError(error);
      }

      ... on  and on... 
  }

Bunu optimize edebilirim ...

  public void ValidateCustomer(Customer customer){

      void _validate(string value, string error){
           if(!string.IsNullOrWhitespace(value)){

              // i can easily reference customer here
              customer.ValidationErrors.Add(error);

              ErrorLogger.Log(error);
              throw new ValidationError(error);                   
           }
      }

      _validate(customer.FirstName, "Firstname cannot be empty");
      _validate(customer.LastName, "Lastname cannot be empty");
      ... on  and on... 
  }

4
@ZoharPeled Eh .. yayınlanmıştır kod yapar işlevi için bir kullanımını gösterir .. :)
Rob

2
@ColinM'in faydalarından biri, anonim işlevin değişkenlere 'ana bilgisayarından' kolayca erişebilmesidir.
mjwills

6
C # -speak'te bunun aslında anonim bir işlev olarak adlandırıldığından emin misiniz? Yani bir isim var gibi gözüküyor _AnonymousFunctionya da sadece _ben hakiki bir anonim işlev gibi bir şey olmasını beklediğiniz süre (x,y) => x+y. Bunu yerel bir işlev olarak adlandırırım, ancak C # terminolojisine alışkın değilim.
chi

12
Açıkçası, kimsenin işaret etmediği gibi, bu kod parçacığı bir yineleyici olduğu için yerel işlevi kullanıyor (verime dikkat edin) ve bu nedenle tembel olarak çalışıyor. Yerel işlev olmadan, ya girdi doğrulamasının ilk kullanımda gerçekleştiğini kabul etmeniz ya da çok az bir nedenden ötürü yalnızca başka bir yöntem tarafından çağrılan bir yönteme sahip olmanız gerekir.
Phoshi

6
@ColinM Gönderilen kuksmen örneği aslında bunun nihayet uygulanmasının ana nedenlerinden biridir - ile bir işlev yaptığınızda yield return, numaralandırılana kadar hiçbir kod çalıştırılmaz. Örneğin argümanları hemen doğrulamak istediğiniz için bu istenmeyen bir durumdur. Bunu C # 'da yapmanın tek yolu, yöntemi iki yönteme ayırmaktır - biri yield returns ile diğeri olmadan. Satır içi yöntemler, kullanım yieldyöntemini içeride bildirmenize olanak tanır , dağınıklıktan ve bir yöntemin kesinlikle üst öğesi için dahili olan ve yeniden kullanılamaz olan olası kötüye kullanımından kaçınmanıza olanak tanır .
Luaan

24

Daha basit bir örneği düşünün

void Main()
{
    Console.WriteLine(Foo()); // Prints 5
}

public static int Foo()
{
    return _();

    // declare the body of _()
    int _()
    {
        return 5;
    }
}

_() return ifadesini içeren yöntem içinde bildirilen yerel bir işlevdir.


3
Evet, yerel işlevler hakkında biliyorum, beni kandıran biçimlendirmeydi ... umarım bu standart olmaz.
kuskmen

20
Aynı satırdan başlayan fonksiyon bildirimini mi kastediyorsunuz? Eğer öyleyse, katılıyorum, bu korkunç!
Stuart

3
Evet, demek istediğim buydu.
kuskmen

9
Bu isimlendirmenin dışında alt çizgi de korkunç
Icepickle

1
@AkashKava: Soru yasal C # olup olmadığı değil, kodun bu şekilde biçimlendirildiğinde anlaşılması kolay (ve dolayısıyla bakımı kolay ve okuması hoş) olup olmadığıdır. Kişisel tercihler bir rol oynar, ancak Stuart'a katılma eğilimindeyim.
PJTraill
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.