C # รายการของวัตถุฉันจะได้รับผลรวมของคุณสมบัติได้อย่างไร


158

ฉันมีรายการของวัตถุ คุณสมบัติหนึ่งของรายการวัตถุแต่ละรายการเป็นจำนวนเงิน ฉันจะได้รับจำนวนเงินได้อย่างไร

หากรายการของฉันเป็นประเภทที่สองฉันอาจทำสิ่งนี้:

double total = myList.Sum();

อย่างไรก็ตามฉันต้องการสิ่งที่คล้ายกับนี้ แต่ไวยากรณ์นี้ไม่ถูกต้อง

double total = myList.amount.Sum();

ฉันจะทำสิ่งนี้ให้สำเร็จได้อย่างไร? ฉันชอบที่จะใช้ฟังก์ชัน Sum หากเป็นไปได้แทนที่จะวนซ้ำและคำนวณค่า

คำตอบ:


310
using System.Linq;

...

double total = myList.Sum(item => item.Amount);

12
นี่เร็วกว่าปล่อยให้คนอื่นสนใจหรือไม่?
Coops

4
ฉันสนใจคำถามของ @ CodeBlend การคำนวณนี้จะเร็วกว่าสำหรับลูปหรือไม่
rex

23
@Coops - เพื่อตอบคำถามของคุณ ... โดยใช้รายการที่มี 100,000 ออบเจ็กต์แต่ละออบเจ็กต์มีคุณสมบัติเดี่ยวของประเภทข้อมูลเป็นสองเท่ารวมถึงคุณสมบัติ 1,000 ครั้งโดยใช้วิธีแก้ปัญหาข้างต้น (myList.Sum) ใช้เวลา 2.44 วินาทีเทียบกับ 0.98 วินาทีโดยใช้ foreach . เวลาที่ผ่านไปถูกวัดโดยใช้คลาสนาฬิกาจับเวลาเพื่อความแม่นยำ ดังนั้น foreach เร็วกว่า 2x เร็วกว่าการใช้ myList.Sum
Joe Gayetty

36

และถ้าคุณจำเป็นต้องทำในรายการที่ตรงกับเงื่อนไขเฉพาะ ...

double total = myList.Where(item => item.Name == "Eggs").Sum(item => item.Amount);

11

ทางเลือกอื่น:

myPlanetsList.Select(i => i.Moons).Sum();

5
แทนที่จะใช้.Select(i => i.Moons).Sum()คุณสามารถใช้งานได้.Sum(i => i.Moons)
Mason

1
@ Mason ใช่มั้ยและนั่นคือวิธีที่ Alex เข้าหาปัญหาในคำตอบก่อนหน้าของเขาดังนั้นฉันจึงให้วิธีที่แตกต่างในการทำสิ่งเดียวกัน
UsefulBee

1
อาใช่ขอโทษด้วยความเข้าใจผิดของฉัน
Mason

ฉันขอขอบคุณที่นี่เป็นคำตอบที่ไม่เหมือนใคร แต่ประสิทธิภาพจะไม่ประสบหรือไม่
Peter Lenjo

2

นี่คือตัวอย่างรหัสที่คุณสามารถใช้เพื่อทำการทดสอบดังกล่าว:

var f = 10000000;
var p = new int[f];

for(int i = 0; i < f; ++i)
{
    p[i] = i % 2;
}

var time = DateTime.Now;
p.Sum();
Console.WriteLine(DateTime.Now - time);

int x = 0;
time = DateTime.Now;
foreach(var item in p){
   x += item;
}
Console.WriteLine(DateTime.Now - time);

x = 0;
time = DateTime.Now;
for(int i = 0, j = f; i < j; ++i){
   x += p[i];
}
Console.WriteLine(DateTime.Now - time);

ตัวอย่างเดียวกันสำหรับวัตถุที่ซับซ้อนคือ:

void Main()
{
    var f = 10000000;
    var p = new Test[f];

    for(int i = 0; i < f; ++i)
    {
        p[i] = new Test();
        p[i].Property = i % 2;
    }

    var time = DateTime.Now;
    p.Sum(k => k.Property);
    Console.WriteLine(DateTime.Now - time);

    int x = 0;
    time = DateTime.Now;
    foreach(var item in p){
        x += item.Property;
    }
    Console.WriteLine(DateTime.Now - time);

    x = 0;
    time = DateTime.Now;
    for(int i = 0, j = f; i < j; ++i){
        x += p[i].Property;
    }
    Console.WriteLine(DateTime.Now - time);
}

class Test
{
    public int Property { get; set; }
}

ผลลัพธ์ของฉันเมื่อปิดการปรับแต่งคอมไพเลอร์คือ:

00:00:00.0570370 : Sum()
00:00:00.0250180 : Foreach()
00:00:00.0430272 : For(...)

และสำหรับการทดสอบครั้งที่สองคือ:

00:00:00.1450955 : Sum()
00:00:00.0650430 : Foreach()
00:00:00.0690510 : For()

ดูเหมือนว่า LINQ โดยทั่วไปจะช้ากว่า foreach (... ) แต่สิ่งที่แปลกสำหรับฉันคือ foreach (... ) ดูเหมือนว่าจะเร็วกว่าลูป


2
สำหรับการอ้างอิงในอนาคตมีลักษณะที่StopwatchในSystem.Diagnosticsขณะที่มันเป็นบันทึกเวลาที่มีประสิทธิภาพสูง (ฉันไม่ได้ลงคะแนนให้คุณ btw)
Meirion Hughes

1
ห้ามใช้DateTime.Nowสำหรับการวัด มันมีประสิทธิภาพแย่มากเพราะมันกลับมาตามเวลาท้องถิ่นเสมอ DateTime.UtcNowเร็วกว่า; อย่างไรก็ตามมันยังไม่ได้ใช้ความละเอียดสูงเช่นเดียวกับStopwatchคลาส
GyörgyKőszeg

3
นี่ไม่ได้ตอบคำถาม
Mark Pattison

ตกลงขอบคุณสำหรับเคล็ดลับ คะแนนสามารถทำซ้ำได้มากดังนั้นฉันจึงสันนิษฐานว่ามติดังกล่าวเพียงพอแล้ว
Puchacz

2
ในขณะที่ความตั้งใจของคุณดี - ทำถูกต้อง - คุณไม่ได้ตอบคำถามอย่างชัดเจน ฉันขอแนะนำให้คุณเปลี่ยนเป็น: "นี่คือวิธีที่คุณทำได้" และ "นี่คือประสิทธิภาพของซีพียูของแต่ละตัวเลือก" ในหลักการถ้าคุณอธิบายวิธีการทดสอบของคุณคุณไม่จำเป็นต้องแสดงรหัส
Meirion Hughes
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.