LINQ Stil tercihi [kapalı]


21

LINQ'u her gün programlamamda çok kullanmaya başladım. Aslında, nadiren, hiç değilse, açık bir döngü kullanıyorum. Ancak, artık SQL benzeri sözdizimini kullanmadığımı tespit ettim. Sadece uzantı fonksiyonlarını kullanıyorum. Yani daha sonra söyleyerek:

from x in y select datatransform where filter 

Kullanırım:

x.Where(c => filter).Select(c => datatransform)

Hangi LINQ stilini tercih edersiniz ve ekibinizdeki diğerleri nelerdir?


5
Resmi MS duruşunun , sorgu sözdiziminin tercih edilebilir olduğu hususuna dikkat etmek gerekir .
R0MANARMY

1
Sonunda önemli değil. Önemli olan kodun anlaşılabilir olmasıdır. Bir durumda bir, diğerinde farklı bir durumda daha iyi olabilir. O zaman uygun olanı kullanın.
ChrisF

İkinci örneğinizin zamanın% 95'ini kullandığım lambda sözdizimi olarak adlandırıldığını düşünüyorum. Diğer% 5'i kullandığım sorgu sözdizimini kullanıyorum, katıldığımda, lambda sözdizimi birleşimlerine geçmeye çalışıyorum, ancak diğerleri gibi dağınıklığa dikkat çekti.
Muffin Man

Yanıtlar:


26

Microsoft’un MSDN belgelerine göre duruşunun sorgu sözdiziminin tercih edilebilir olması ne yazık ki, çünkü asla kullanmam, ancak LINQ yöntemi sözdizimini her zaman kullanıyorum. Kalbimin içeriğine tek bir sorgu gönderebilmeyi seviyorum. Karşılaştırmak:

var products = from p in Products
               where p.StockOnHand == 0
               select p;

Kime:

var products = Products.Where(p => p.StockOnHand == 0);

Daha hızlı, daha az çizgi ve gözlerime daha temiz görünüyor. Sorgu sözdizimi de standart LINQ operatörlerinin tümünü desteklemiyor. Son zamanlarda yaptığım bir örnek sorgu şuna benziyordu:

var itemInfo = InventoryItems
    .Where(r => r.ItemInfo is GeneralMerchInfo)
    .Select(r => r.ItemInfo)
    .Cast<GeneralMerchInfo>()
    .FirstOrDefault(r => r.Xref == xref);

Bildiğim kadarıyla, bu sorguyu sorgu sözdizimini kullanarak (mümkün olduğu ölçüde) çoğaltmak şöyle olurdu:

var itemInfo = (from r in InventoryItems
                where r.ItemInfo is GeneralMerchInfo
                select r.ItemInfo)
                .Cast<GeneralMerchInfo>()
                .FirstOrDefault(r => r.Xref == xref);

Bana daha okunaklı görünmüyor ve yine de yöntem sözdizimini nasıl kullanacağınızı bilmeniz gerekiyor. Şahsen ben gerçekten bildirimsel tarzı hayranım LINQ mümkün ve mümkün olduğu her durumda - belki de bazen benim zararıma. Durumda, yöntem sözdizimi ile böyle bir şey yapabilir:

// projects an InventoryItem collection with total stock on hand for each GSItem
inventoryItems = repository.GSItems
    .Select(gsItem => new InventoryItem() {
        GSItem = gsItem,
        StockOnHand = repository.InventoryItems
            .Where(inventoryItem => inventoryItem.GSItem.GSNumber == gsItem.GSNumber)
            .Sum(r => r.StockOnHand)
     });

Yukarıdaki kodun, projeye iyi belgelendirme olmadan gelen birisinin anlaşmasının zor olacağını ve LINQ'da sağlam bir geçmişleri yoksa, yine de anlayamayacaklarını hayal ediyorum. Yine de, yöntem sözdizimi, hızlı bir şekilde (kod satırları açısından), aksi halde çok fazla sıkıcı foreach döngüsü alabilecek birden fazla koleksiyon hakkında toplu bilgi edinmek için bir sorgu tasarlama konusunda oldukça güçlü bir yetenek sunar. Böyle bir durumda, yöntem sözdizimi ondan ne almak için ultra kompakt. Sorgu sözdizimi ile bunu yapmaya çalışmak oldukça hızlı bir şekilde hantallaşabilir.


Seçim içinde yapabileceğiniz seçim ancak maalesef LINQ yöntemlerini kullanmaya başlamadan en iyi X kayıtları almayı belirtemezsiniz. Bu, özellikle sadece bir kayda ihtiyacınız olduğunu ve tüm sorguyu parantez içine almak zorunda kaldığınızı bildiğiniz yerlerde can sıkıcı bir durum.
Ziv

2
Sadece yapabileceğiniz kayıt için Where () yerine Select (x => x.ItemInfo) .OfType <GeneralMerchInfo> () öğesini seçin (daha hızlı olduğuna inanıyorum (2n büyük O) n * 2m yerine sanırım). Ancak tamamen haklısınız, lambda sözdizimi okunabilirlik açısından çok daha iyidir.
Ed James,

16

İşlevsel sözdizimini göze daha hoş buluyorum. Bunun tek istisnası ikiden fazla gruba katılmam gerekirse. Join () çok hızlı bir şekilde delirir.


Anlaştım ... Katılmam dışında (belirtildiği gibi) uzatma yöntemlerinden görünüş ve okunabilirliği daha çok tercih ederim. Bileşen satıcıları (örneğin, Telerik), uzantı yöntemlerini çok kullanır. Düşündüğüm örnek ASP.NET MVC'deki Rad Kontrolleri. Bunları kullanmak / okumak için uzatma yöntemlerini kullanma konusunda çok uzman olmanız gerekir.
Catchops

Bunu söylemeye geldim. Katılmadan önce genellikle lambda kullanıyorum. Bir kez üye olduktan sonra, LINQ sözdizimi daha okunaklı olma eğilimindedir.
Sean,

10

Başka bir cevap eklemek için hiç geç oldu mu?

Bir ton LINQ-nesne kodu yazdım ve en azından bu etki alanında, hangisinin daha basit kod için yapılacağını kullanmak için her iki sözdizimini anlamanın iyi olduğunu iddia ediyorum - bu her zaman nokta sözdizimi değildir.

Elbette, nokta sözdiziminin gitme yolu IS olduğu zamanlar vardır - diğerleri bu vakaların birkaçını sağlamıştır; Ancak, anlamaların kısa değiştiğini düşünüyorum - eğer istersen kötü bir rap verilir. Bu yüzden, anlamaların yararlı olduğuna inandığım bir örnek vereceğim.

İşte bir basamaklı değiştirme bulmacasına bir çözüm: (LINQPad kullanılarak yazılmış, ancak bir konsol uygulamasında tek başına durabilir)

// NO
// NO
// NO
//+NO
//===
// OK

var solutions =
    from O in Enumerable.Range(1, 8) // 1-9
                    //.AsQueryable()
    from N in Enumerable.Range(1, 8) // 1-9
    where O != N
    let NO = 10 * N + O
    let product = 4 * NO
    where product < 100
    let K = product % 10
    where K != O && K != N && product / 10 == O
    select new { N, O, K };

foreach(var i in solutions)
{
    Console.WriteLine("N = {0}, O = {1}, K = {2}", i.N, i.O, i.K);
}

//Console.WriteLine("\nsolution expression tree\n" + solutions.Expression);

... hangi çıktılar:

N = 1, O = 6, K = 4

Çok da kötü değil, mantık doğrusal akıyor ve bunun tek bir doğru çözümle geldiğini görebiliyoruz. Bu bulmacanın elle çözmesi yeterince kolaydır: 3>> N0 ve O> 4 * N'nin 8> = O> = 4 olduğu anlamına gelir. Bu, elle test etmek için maksimum 10 vaka olduğu anlamına gelir (2 için N-by- 5 O). Yeterince başıboş oldum - bu bulmaca LINQ gösterim amacıyla sunulmuştur.

Derleyici Dönüşümleri

Derleyicinin bunu eşdeğer nokta sözdizimine çevirmek için yaptığı çok şey var. Her zamanki yanında ikinci ve sonraki frommaddeleri dönüştü SelectManyaramalar Elimizdeki lethaline maddelerini Selectkullanmak her ikisi de projeksiyonlar ile çağrılar, şeffaf-tanımlayıcıları . Göstermek üzere olduğum gibi, bu tanımlayıcıları nokta sözdiziminde adlandırmak zorunda kalmak, bu yaklaşımın okunabilirliğinden uzaklaşıyor.

Derleyicinin bu kodu nokta sözdizimine çevirmesinde ne yaptığını göstermek için bir numaram var. Yukarıdaki iki yorum satırını açıp tekrar çalıştırırsanız, aşağıdaki çıktıyı alırsınız:

N = 1, O = 6, K = 4

çözüm ifadesi ağacı System.Linq.Enumerable + d_ b8.SelectMany (O => Aralık (1, 8), (O, N) => yeni <> f _AnonymousType0 2(O = O, N = N)).Where(<>h__TransparentIdentifier0 => (<>h__TransparentIdentifier0.O != <>h__TransparentIdentifier0.N)).Select(<>h__TransparentIdentifier0 => new <>f__AnonymousType12 (<> h_ TransparentIdentifier0 = <> h _TransparentIdentifier0, NO = ((10 * <> h_ TransparentIdentifier0.N) + '' H _TransparentIdentifier0.O))). Select (<> h_ TransparentIdentifier1 => yeni <> f _AnonymousType2 2(<>h__TransparentIdentifier1 = <>h__TransparentIdentifier1, product = (4 * <>h__TransparentIdentifier1.NO))).Where(<>h__TransparentIdentifier2 => (<>h__TransparentIdentifier2.product < 100)).Select(<>h__TransparentIdentifier2 => new <>f__AnonymousType32 (<> h_ TransparentIdentifier2 = <> h _TransparentIdentifier2, K = ( <> h_ TransparentIdentifier2.product% 10))).) Nerede (<> h _TransparentIdentifier3 => ((((((<> h_ TransparentIdentifier3.K! = <> h _TransparentIdentifier3. <> h_ TransparentIdentifier2)h _TransparentIdentifier1. <> h_TransparentIdentifier0.O) AndAlso (<> h _TransparentIdentifier3.K! = <> H_ TransparentIdentifier3. <> Sa _TransparentIdentifier2. <> H_ TransparentIdentifier1. <> H _TransparentIdentifier0.N)) AndAlso ((<> h_ TransparentIdentifier3. <> H _TransparentIdentifier2. ürün / 10) == <> h_ TransparentIdentifier3. <> h _TransparentIdentifier2. <> h_ TransparentIdentifier1. <> h _TransparentIdentifier0.O))). Select (<> h_ TransparentIdentifier3 => yeni <> f _AnonymousType4`3 (n = < > h_ TransparentIdentifier3. <> h _TransparentIdentifier2. <> h_ TransparentIdentifier1. <> h _TransparentIdentifier0.N,O = <> h_ TransparentIdentifier3. <> H_TransparentIdentifier2. <> H_ TransparentIdentifier1. <> H _TransparentIdentifier0.O, K = <> h__TransparentIdentifier3.K))

Her LINQ operatörünü yeni bir satıra koymak, "konuşulamayacak" tanımlayıcıları "konuşabildiklerimize" çevirmek, anonim türleri bilinen biçimlerine değiştirmek ve derleyicinin bir eşdeğerde gelmesi için elde ettiği dönüşümleri AndAlsogöstermek için ifade ağacı dilini değiştirmek &&nokta sözdiziminde:

var solutions = 
    Enumerable.Range(1,8) // from O in Enumerable.Range(1,8)
        .SelectMany(O => Enumerable.Range(1, 8), (O, N) => new { O = O, N = N }) // from N in Enumerable.Range(1,8)
        .Where(temp0 => temp0.O != temp0.N) // where O != N
        .Select(temp0 => new { temp0 = temp0, NO = 10 * temp0.N + temp0.O }) // let NO = 10 * N + O
        .Select(temp1 => new { temp1 = temp1, product = 4 * temp1.NO }) // let product = 4 * NO
        .Where(temp2 => temp2.product < 100) // where product < 100
        .Select(temp2 => new { temp2 = temp2, K = temp2.product % 10 }) // let K = product % 10
        .Where(temp3 => temp3.K != temp3.temp2.temp1.temp0.O && temp3.K != temp3.temp2.temp1.temp0.N && temp3.temp2.product / 10 == temp3.temp2.temp1.temp0.O)
        // where K != O && K != N && product / 10 == O
        .Select(temp3 => new { N = temp3.temp2.temp1.temp0.N, O = temp3.temp2.temp1.temp0.O, K = temp3.K });
        // select new { N, O, K };

foreach(var i in solutions)
{
    Console.WriteLine("N = {0}, O = {1}, K = {2}", i.N, i.O, i.K);
}

Çalıştırırsanız, tekrar çıktı olduğunu doğrulayabilirsiniz:

N = 1, O = 6, K = 4

... ama hiç böyle kod yazacak mısın?

Bahse girerim cevabı NONBHN (Sadece Hayır, Ama Cehennem Hayır!) - çünkü sadece çok karmaşık. Tabii ki "temp0" .. "temp3" den daha anlamlı bazı tanımlayıcı isimler bulabilirsin, fakat asıl nokta, koda bir şey eklemiyor olmaları - kodun daha iyi performans göstermesini sağlamazlar, kodun daha iyi okunmasını sağlayın, yalnızca kodu çirkinleştirirler ve el ile yapıyor olsaydınız, doğru yapmadan önce bir veya üç kez karıştırırsınız. Ayrıca, "isim oyununu" oynamak, anlamlı tanımlayıcılar için yeterince zor, bu yüzden derleyicinin sorgu anlamalarında bana sağladığı isim oyunundan ayrılıyorum.

Bu bulmaca örneği, ciddiye almanız için yeterli gerçek dünya olmayabilir ; ancak, sorgu anlamaların parladığı başka senaryolar vardır:

  • Karmaşıklığı Joinve GroupJoin: Sorgu anlama aralık değişkenlerin kapsam joinmaddelerine aksi anlama sözdiziminde derleme zamanı hataları içine nokta sözdiziminde derlemek olabilir hataları açın.
  • Derleyici anlama dönüşümüne şeffaf bir tanımlayıcı getireceği zaman, anlama değdi. Bu, aşağıdakilerden herhangi birinin kullanımını içerir: çoklu fromcümle, join& join..intocümle ve letcümle.

Memleketimde, anlama kavramını yasa dışı bırakan birden fazla mühendislik mağazasını tanıyorum . Sanırım bu anlama sözdizimi bir araç değil, ancak yararlı bir araç olarak üzücü. Sanırım "Bir tornavida ile yapamayacağınız şeyler var. Bir keskiyle yapamayacağınız şeyler var. Çünkü bir tornavidayı bir keski olarak kullanabilirsiniz, keskiler bundan böyle kralın kararnamesi altında yasaklanır."


-1: Vay canına. OP biraz öneri arıyordu. Bir roman çalkaladın! Bunu biraz sıkılaştırır mısın lütfen?
Jim G.

8

Tavsiyem, tüm ifadenin anlama sözdiziminde yapılabildiği durumda sorgu anlama sözdizimini kullanmaktır. Yani, tercih ederim:

var query = from c in customers orderby c.Name select c.Address;

için

var query = customers.OrderBy(c=>c.Name).Select(c=>c.Address);

Ama ben tercih ederim

int count = customers.Where(c=>c.City == "London").Count();

için

int count = (from c in customers where c.City == "London" select c).Count();

Keşke ikisini karıştırmayı daha da güzelleştiren bir sözdizimi ile gelseydik. Gibi bir şey:

int count = from c in customers 
            where c.City == "London" 
            select c 
            continue with Count();

Ama ne yazık ki yapmadık.

Ama temelde, bu bir tercih meselesi. Size ve iş arkadaşlarınıza daha iyi görünen birini yapın.


3
Alternatif olarak, bir kavrayışı diğer LINQ operatör çağrılarından bir "tanıtıcı değişken" yeniden düzenleme ile ayırmayı düşünebilirsiniz. örneğin,var londonCustomers = from c in ...; int count = londonCustomers.Count();
devgeezer

3

SQL benzeri başlamak için iyi bir yoldur. Ancak, sınırlı olduğu için (yalnızca şu anki dilinizin desteklediği yapıları destekler), sonunda geliştiriciler uzantı yöntemleri stiline gider.

SQL tarzı ile kolayca uygulanabilecek bazı durumlar olduğunu belirtmek isterim.

Ayrıca her iki yolu da bir sorguda birleştirebilirsiniz.


2

Sorgu gibi olsa ortada bir değişken tanımlamak zorunda olmadığım sürece sorgu olmayan sözdizimini kullanma eğilimindeyim

from x in list
let y = x.DoExpensiveCalulation()
where y > 42
select y

ancak sorgu olmayan sözdizimini şöyle yazarım

x.Where(c => filter)
 .Select(c => datatransform)

2

Sıralama nedeniyle daima uzatma fonksiyonlarını kullanırım. Basit örneğinizi alın - SQL'de, önce seçme işlemini yazdınız - gerçekte, ilk önce nerede yapıldığına rağmen. Eklenti yöntemlerini kullanarak yazdığınızda, kontrolde kendimi çok daha fazla hissediyorum. Sunulan şey hakkında Intellisense alıyorum, olayları olduğu sırayla yazıyorum.


"Sorgu anlama" sözdiziminde, sayfadaki sıralamanın işlemlerin gerçekleştiği sırayla aynı olduğunu göreceksiniz. LINQ, SQL'den farklı olarak ilk önce "select" i seçmedi.
Eric Lippert

1

Uzantı işlevini de seviyorum.

Belki de aklımda sözdizimi sıçramasından daha az olduğu içindir.

Özellikle linq api olan üçüncü parti çerçeveleri kullanıyorsanız, göze daha okunaklı geliyor.


0

İşte takip ettiğim sezgisel çözüm:

Katıldıysanız, lambdas üzerinden LINQ ifadelerini tercih edin.

Katılmalı lambdaların dağınık göründüğünü ve okunmasının zor olduğunu düşünüyorum.

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.