LINQ: เลือกวัตถุและเปลี่ยนคุณสมบัติบางอย่างโดยไม่ต้องสร้างวัตถุใหม่


218

ฉันต้องการเปลี่ยนคุณสมบัติบางอย่างของวัตถุผลลัพธ์การสืบค้น LINQ โดยไม่ต้องสร้างวัตถุใหม่และตั้งค่าคุณสมบัติทั้งหมดด้วยตนเอง เป็นไปได้ไหม

ตัวอย่าง:

var list = from something in someList
           select x // but change one property

7
ฉันเขียนวิธีการขยายตามคำตอบของ JaredPar ด้านล่างที่ช่วยให้คุณทำสิ่งนี้ได้สำเร็จโดยใช้รูปแบบการประกาศเหมือนตัวอย่างของฉัน ลองใช้งานได้ที่: blog.robvolk.com/2009/05/…
Rob Volk

3
@RobVolk การเชื่อมโยงปรากฏเป็นตาย - มีใหม่หรือไม่ นี่คือไฟล์เก็บถาวร - LINQ: เลือกวัตถุ แต่เปลี่ยนคุณสมบัติบางอย่างโดยไม่สร้างวัตถุใหม่
KyleMit

blog.robvolk.com/2009/05/…ไม่พบข้อผิดพลาด DNS
Kiquenet

1
ขอโทษด้วยกับเรื่องนั้น! ที่อยู่ที่ถูกต้องของihere
Rob Volk

1
ดูเพิ่มเติมที่: stackoverflow.com/questions/47836019/…
Revious

คำตอบ:


380

ฉันไม่แน่ใจว่าไวยากรณ์การสืบค้นคืออะไร แต่นี่คือตัวอย่างการแสดงออกของ LINQ ที่ขยายเพิ่ม

var query = someList.Select(x => { x.SomeProp = "foo"; return x; })

สิ่งนี้จะใช้วิธี vs และ expression ที่ไม่ระบุชื่อ สิ่งนี้อนุญาตให้คุณใช้หลายประโยคในแลมบ์ดาเดียว ดังนั้นคุณสามารถรวมการดำเนินการทั้งสองของการตั้งค่าคุณสมบัติและส่งคืนวัตถุในวิธีการที่ค่อนข้างกระชับ


4
@ Rob ไม่ง่าย ไวยากรณ์เพื่อให้การทำงานนั้นเป็น ... อ่านไม่ได้ที่ดีที่สุด var query = จากมันในรายการ select ((Func <Foo>) (() => {it.x = 42; return it;})) ();
JaredPar

35
ฉันได้รับข้อผิดพลาด "นิพจน์แลมบ์ดาที่มีเนื้อหาข้อความไม่สามารถแปลงเป็นต้นไม้นิพจน์ได้" ไม่ใช่สำหรับ LINQ ไปยัง SQL คำแนะนำใด ๆ
surya

2
@spiderdevil +1 สำหรับคุณ - คุณไม่เพียงแค่เกลียดความไม่ลงรอยกันใช่ไหม นี่ไม่ใช่ครั้งแรกที่ฉันพบสถานการณ์ที่โค้ดใช้ได้กับ Linq to Object เท่านั้นและไม่ใช่ Linq ถึง SQL / EF นี่อาจเป็นวิธีแก้ปัญหาที่นี่แต่ฉันยังไม่ได้ลองใช้เพราะยังไม่มีเวลา
dyslexicanaboko

11
@surya นั่นคือข้อความที่ฉันได้รับเมื่อลองใช้กับ Linq ไปยัง SQL เพิ่มToList()ก่อนที่ดูเหมือนว่าจะทำเคล็ดลับ ไม่แน่ใจว่าทำไมคุณถึงได้รับ ถ้าเป็น Entity Framework มันจะไม่ทำงานกับสิ่งนั้น
Raziel

5
@surya เมื่อคุณเรียก ToList () คุณจะนำข้อมูลกลับเข้าสู่หน่วยความจำ ดังนั้นมันจึงไม่ใช่ Linq ไปยัง SQL อีกต่อไป
โอ๊ก

60

หากคุณเพียงต้องการอัปเดตสถานที่ให้บริการในองค์ประกอบทั้งหมดแล้ว

someList.All(x => { x.SomeProp = "foo"; return true; })

3
ใน EF (Entity Framework): เพื่อแทนที่คุณสมบัติบนวัตถุทั้งหมดของ IEnumerable คำตอบที่ยอมรับนั้นใช้ได้สำหรับฉัน รหัสที่ใช้งานได้สำหรับฉัน: var myList = _db.MyObjects.Where (o => o.MyProp == "bar") AsEnumerable () เลือก (x => {x.SomeProp = "foo"; return x;}) ;
firepol

32

ฉันชอบอันนี้ มันสามารถรวมกับคำสั่ง linq อื่น ๆ

from item in list
let xyz = item.PropertyToChange = calcValue()
select item

24

ไม่ควรมีเวทมนตร์ LINQ ใด ๆ ที่ขัดขวางไม่ให้คุณทำเช่นนี้ อย่าใช้การฉายภาพ แต่จะส่งคืนประเภทที่ไม่ระบุตัวตน

User u = UserCollection.FirstOrDefault(u => u.Id == 1);
u.FirstName = "Bob"

ที่จะแก้ไขวัตถุจริงเช่นเดียวกับ:

foreach (User u in UserCollection.Where(u => u.Id > 10)
{
    u.Property = SomeValue;
}

ฉันชอบสิ่งนี้คำถามของฉันเป็นตัวอย่างแรกจะเกิดอะไรขึ้นถ้าไม่มี u> 10 บางตัวในรายการ ฉันเพิ่มการตรวจสอบที่ว่างเปล่าและดูเหมือนว่าจะใช้งานได้ นอกจากนี้ LHS ฉันตั้งชื่อคุณเป็น v +1 ด้วย รัดกุมมาก
desaivv

11

ไม่สามารถใช้ตัวดำเนินการแบบสอบถามมาตรฐานได้ - เป็น Language Query ไม่ใช่ Language Integrated Update แต่คุณสามารถซ่อนอัปเดตของคุณในวิธีการขยาย

public static class UpdateExtension
{
    public static IEnumerable<Car> ChangeColorTo(
       this IEnumerable<Car> cars, Color color)
    {
       foreach (Car car in cars)
       {
          car.Color = color;
          yield return car;
       }
    }
}

ตอนนี้คุณสามารถใช้มันได้ดังต่อไปนี้

cars.Where(car => car.Color == Color.Blue).ChangeColorTo(Color.Red);

1
ไม่ใช่ว่าคุณต้องการอัปเดตคอลเลกชันพื้นฐาน เราต้องการอัปเดตคุณสมบัติการรวบรวมผลลัพธ์
Michael Brennt

4
ดี LINQ แทนที่ SQL ซึ่งย่อมาจาก Structured Query Language แต่ก็ยังคงความสามารถในการตีแผ่ความสามารถในการUPDATE, และDELETE INSERTดังนั้นฉันจะไม่เถียงว่าความหมายคือสิ่งที่ป้องกันฟังก์ชั่นนี้
KyleMit

5

หากคุณต้องการอัปเดตรายการที่มีWhereประโยคการใช้. ที่ไหน (... ) จะตัดทอนผลลัพธ์ของคุณหากคุณ:

mylist = mylist.Where(n => n.Id == ID).Select(n => { n.Property = ""; return n; }).ToList();

คุณสามารถอัปเดตรายการที่ต้องการในรายการดังนี้:

mylist = mylist.Select(n => { if (n.Id == ID) { n.Property = ""; } return n; }).ToList();

ส่งคืนรายการเสมอแม้ว่าคุณจะไม่ทำการเปลี่ยนแปลงใด ๆ วิธีนี้มันจะถูกเก็บไว้ในรายการ


1

บ่อยครั้งที่เราพบเจอสิ่งนี้ที่เราต้องการรวมค่าดัชนีและตัวบ่งชี้ที่หนึ่งและสุดท้ายในรายการโดยไม่ต้องสร้างวัตถุใหม่ สิ่งนี้ช่วยให้คุณทราบตำแหน่งของรายการในรายการการแจงนับ ฯลฯ โดยไม่ต้องแก้ไขคลาสที่มีอยู่แล้วรู้ว่าคุณอยู่ที่รายการแรกในรายการหรือสุดท้าย

foreach (Item item in this.Items
    .Select((x, i) => {
    x.ListIndex = i;
    x.IsFirst = i == 0;
    x.IsLast = i == this.Items.Count-1;
    return x;
}))

คุณสามารถขยายชั้นเรียนโดยใช้:

public abstract class IteratorExtender {
    public int ListIndex { get; set; }
    public bool IsFirst { get; set; } 
    public bool IsLast { get; set; } 
}

public class Item : IteratorExtender {}

0

เนื่องจากฉันไม่พบคำตอบที่นี่ซึ่งฉันพิจารณาวิธีแก้ปัญหาที่ดีที่สุดมาที่นี่ในแบบของฉัน:

การใช้ "เลือก" เพื่อปรับเปลี่ยนข้อมูลเป็นไปได้ แต่มีเพียงกลอุบาย อย่างไรก็ตาม "Select" ไม่ได้ทำเพื่อสิ่งนั้น เพียงแค่เรียกใช้การปรับเปลี่ยนเมื่อใช้กับ "ToList" เนื่องจาก Linq ไม่ได้ดำเนินการก่อนที่จะมีการต้องการข้อมูล อย่างไรก็ตามทางออกที่ดีที่สุดคือการใช้ "foreach" ในรหัสต่อไปนี้คุณสามารถดู:

    class Person
    {
        public int Age;
    }

    class Program
    {
        private static void Main(string[] args)
        {
            var persons = new List<Person>(new[] {new Person {Age = 20}, new Person {Age = 22}});
            PrintPersons(persons);

            //this doesn't work:
            persons.Select(p =>
            {
                p.Age++;
                return p;
            });
            PrintPersons(persons);

            //with "ToList" it works
            persons.Select(p =>
            {
                p.Age++;
                return p;
            }).ToList();
            PrintPersons(persons);

            //This is the best solution
            persons.ForEach(p =>
            {
                p.Age++;
            });
            PrintPersons(persons);
            Console.ReadLine();
        }

        private static void PrintPersons(List<Person> persons)
        {
            Console.WriteLine("================");
            foreach (var person in persons)
            {
                Console.WriteLine("Age: {0}", person.Age);
            ;
            }
        }
    }

ก่อนหน้า "foreach" คุณสามารถเลือก linq ได้ด้วย ...


0
var item = (from something in someList
       select x).firstordefault();

จะได้รับitemแล้วคุณสามารถทำได้item.prop1=5;เพื่อเปลี่ยนคุณสมบัติเฉพาะ

หรือคุณต้องการรับรายการจากฐานข้อมูลและให้มันเปลี่ยนคุณสมบัติของprop1แต่ละรายการในรายการที่ส่งคืนเป็นค่าที่ระบุหรือไม่ ถ้าเป็นเช่นนั้นคุณสามารถทำได้ (ฉันทำใน VB เพราะฉันรู้ดีกว่า):

dim list = from something in someList select x
for each item in list
    item.prop1=5
next

( listจะมีรายการทั้งหมดที่ส่งคืนพร้อมกับการเปลี่ยนแปลงของคุณ)


ขณะนี้จะใช้งานได้ฉันคิดว่า OP ได้ขอให้เขียนคำสั่ง LINQ โดยไม่ต้องเขียนfor eachลูป
fujiiface


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.