Perbarui semua objek dalam koleksi menggunakan LINQ


500

Apakah ada cara untuk melakukan hal berikut ini menggunakan LINQ?

foreach (var c in collection)
{
    c.PropertyToSet = value;
}

Untuk memperjelas, saya ingin mengulangi setiap objek dalam koleksi dan kemudian memperbarui properti pada setiap objek.

Kasus penggunaan saya adalah saya memiliki banyak komentar pada posting blog, dan saya ingin mengulangi setiap komentar pada posting blog dan mengatur datetime pada posting blog menjadi 10 jam. Saya bisa melakukannya dalam SQL, tetapi saya ingin menyimpannya di lapisan bisnis.


14
Pertanyaan menarik. Secara pribadi saya lebih suka bagaimana Anda mendapatkannya di atas - jauh lebih jelas apa yang terjadi!
noelicus

8
Saya datang ke sini mencari jawaban untuk pertanyaan yang sama, dan memutuskan bahwa itu sama mudahnya, lebih sedikit kode, dan lebih mudah dipahami bagi pengembang masa depan untuk melakukannya seperti yang Anda lakukan pada OP.
Casey Crookston

4
Mengapa Anda ingin melakukannya di LINQ?
Caltor

13
Pertanyaan ini menanyakan hal yang salah, satu-satunya jawaban yang benar adalah: jangan gunakan LINQ untuk mengubah sumber data
Tim Schmelter

Saya memilih untuk menutup pertanyaan ini sebagai di luar topik karena hampir semua jawaban untuk pertanyaan ini secara aktif berbahaya bagi pemahaman programmer baru tentang LINQ.
Tanveer Badar

Jawaban:


842

Meskipun Anda dapat menggunakan ForEachmetode ekstensi, jika Anda ingin menggunakan hanya kerangka yang bisa Anda lakukan

collection.Select(c => {c.PropertyToSet = value; return c;}).ToList();

The ToListdiperlukan dalam rangka untuk mengevaluasi memilih segera karena malas evaluasi .


6
Saya membatalkan ini karena ini solusi yang sangat bagus ... satu-satunya alasan saya menyukai metode ekstensi adalah membuatnya sedikit lebih jelas untuk memahami apa yang sebenarnya terjadi ... namun solusi Anda masih cukup manis
lomaxx

9
Jika koleksi adalah ObservableCollectionucapan, maka mengubah item yang ada daripada membuat daftar baru bisa bermanfaat.
Cameron MacFarland

7
@ Selesaivv ya ini sedikit penyalahgunaan sintaks, jadi Resharper memperingatkan Anda tentang ini.
Cameron MacFarland

46
IMHO, ini jauh lebih ekspresif daripada loop foreach sederhana. ToList () membingungkan karena tidak digunakan untuk apa pun kecuali memaksa evaluasi yang seharusnya ditangguhkan. Proyeksi ini juga membingungkan karena tidak digunakan untuk tujuan yang dimaksudkan; alih-alih, ini digunakan untuk beralih ke elemen-elemen koleksi dan memungkinkan akses ke properti sehingga dapat diperbarui. Satu-satunya pertanyaan di benak saya adalah apakah loop foreach bisa mendapatkan manfaat dari paralelisme menggunakan Paralel. Untuk setiap langkah, tapi itu pertanyaan yang berbeda.
Philippe

37
Jawaban ini adalah praktik terburuk. Tidak pernah melakukan ini.
Eric Lippert

351
collection.ToList().ForEach(c => c.PropertyToSet = value);

36
@SanthoshKumar: Gunakancollection.ToList().ForEach(c => { c.Property1ToSet = value1; c.Property2ToSet = value2; });
Ε Г И І И О

@CameronMacFarland: Tentu saja tidak karena struct tidak dapat diubah. Tetapi jika Anda benar-benar ingin, Anda dapat melakukan ini:collection.ToList().ForEach(c => { collection[collection.IndexOf(c)] = new <struct type>() { <propertyToSet> = value, <propertyToRetain> = c.Property2Retain }; });
Ε Г И І И О

11
Ini memiliki keunggulan dibandingkan jawaban Cameron MacFarland untuk memperbarui daftar yang ada, daripada membuat daftar baru.
Simon Tewsi

7
Wow, jawaban ini benar-benar tidak berguna. Membuat koleksi baru hanya untuk dapat menggunakan loop
Tim Schmelter

@SimonTewsi Karena ini adalah kumpulan objek, daftar harus diperbarui di tempat lagian. Koleksinya akan baru, tetapi objek dalam koleksinya akan sama.
Chris

70

Saya melakukan ini

Collection.All(c => { c.needsChange = value; return true; });

Saya pikir ini adalah cara terbersih untuk melakukannya.
wcm

31
Pendekatan ini tentu saja berhasil tetapi melanggar maksud All()metode ekstensi, yang menyebabkan potensi kebingungan ketika orang lain membaca kode.
Tom Baxter

Pendekatan ini lebih baik. Menggunakan Semua daripada menggunakan setiap loop
UJS

2
Pasti lebih suka ini daripada memanggil ToList () tidak perlu, bahkan jika itu sedikit menyesatkan tentang apa yang menggunakan All () untuk
iupchris10

1
Jika Anda menggunakan koleksi seperti List<>itu, ForEach()metode ini adalah cara yang jauh lebih samar untuk melakukannya. exForEach(c => { c.needsChange = value; })
Dan Is Fiddling By Firelight

27

Saya benar-benar menemukan metode ekstensi yang akan melakukan apa yang saya inginkan dengan baik

public static IEnumerable<T> ForEach<T>(
    this IEnumerable<T> source,
    Action<T> act)
{
    foreach (T element in source) act(element);
    return source;
}

4
bagus :) Lomaxx, mungkin tambahkan contoh sehingga peeps bisa melihatnya di 'action' (boom tish!).
Pure.Krome

2
Ini adalah satu-satunya pendekatan yang berguna jika Anda benar-benar ingin menghindari foreach-loop (untuk alasan apa pun).
Tim Schmelter

@Rango yang masih TIDAK Anda hindari foreachkarena kode itu sendiri mengandung foreachloop
GoldBishop

@GoldBishop yakin, metode menyembunyikan loop.
Tim Schmelter

1
Tautan rusak, sekarang tersedia di: codewrecks.com/blog/index.php/2008/08/13/… . Ada juga komentar blog yang tertaut ke stackoverflow.com/questions/200574 . Pada gilirannya, pertanyaan atas tautan komentar ke blogs.msdn.microsoft.com/ericlippert/2009/05/18/… . Mungkin jawabannya akan lebih sederhana ditulis ulang menggunakan MSDN (Anda masih bisa mengkredit tautan pertama jika Anda mau). Sidenote: Rust memiliki fitur serupa, dan akhirnya menyerah dan menambahkan fungsi yang setara: stackoverflow.com/a/50224248/799204
sourcejedi

15

Menggunakan:

ListOfStuff.Where(w => w.Thing == value).ToList().ForEach(f => f.OtherThing = vauleForNewOtherThing);

Saya tidak yakin apakah ini menggunakan LINQ secara berlebihan atau tidak, tetapi ini berhasil bagi saya ketika ingin memperbarui item tertentu dalam daftar untuk kondisi tertentu.


7

Tidak ada metode ekstensi bawaan untuk melakukan ini. Meskipun mendefinisikan satu cukup lurus ke depan. Di bagian bawah posting adalah metode yang saya definisikan disebut Iterate. Itu bisa digunakan seperti itu

collection.Iterate(c => { c.PropertyToSet = value;} );

Sumber Iterate

public static void Iterate<T>(this IEnumerable<T> enumerable, Action<T> callback)
{
    if (enumerable == null)
    {
        throw new ArgumentNullException("enumerable");
    }

    IterateHelper(enumerable, (x, i) => callback(x));
}

public static void Iterate<T>(this IEnumerable<T> enumerable, Action<T,int> callback)
{
    if (enumerable == null)
    {
        throw new ArgumentNullException("enumerable");
    }

    IterateHelper(enumerable, callback);
}

private static void IterateHelper<T>(this IEnumerable<T> enumerable, Action<T,int> callback)
{
    int count = 0;
    foreach (var cur in enumerable)
    {
        callback(cur, count);
        count++;
    }
}

Apakah Iterate diperlukan, apa yang salah dengan Count, Sum, Avg atau metode ekstensi lain yang ada yang mengembalikan nilai skalar?
AnthonyWJones

2
ini cukup dekat dengan apa yang saya inginkan tetapi sedikit .. terlibat. Posting blog yang saya posting memiliki implementasi yang serupa tetapi dengan lebih sedikit baris kode.
lomaxx

1
The IterateHelper tampaknya berlebihan. Kelebihan yang tidak mengambil indeks akhirnya melakukan lebih banyak pekerjaan tambahan (konversi panggilan balik ke lambda yang mengambil indeks, simpan hitungan yang tidak pernah digunakan). Saya mengerti itu digunakan kembali, tapi itu solusi untuk hanya menggunakan forloop, jadi itu harus efisien.
Cameron MacFarland

2
@Cameron, IterateHelper melayani 2 tujuan. 1) Implementasi tunggal dan 2) memungkinkan ArgumentNullException dilemparkan pada waktu panggilan vs. penggunaan. C # iterator ditunda dieksekusi, memiliki helper mencegah perilaku aneh dari sebuah pengecualian yang dilemparkan selama iterasi.
JaredPar

2
@JaredPar: Kecuali Anda tidak menggunakan iterator. Tidak ada pernyataan hasil.
Cameron MacFarland

7

Meskipun Anda secara khusus meminta solusi LINQ dan pertanyaan ini sudah cukup lama saya memposting solusi non-LINQ. Ini karena LINQ (= query terintegrasi bahasa ) dimaksudkan untuk digunakan untuk kueri pada koleksi. Semua metode LINQ tidak mengubah koleksi yang mendasarinya, mereka hanya mengembalikan yang baru (atau iterator yang lebih tepat ke koleksi baru). Jadi, apa pun yang Anda lakukan misalnya dengan Selecttidak mempengaruhi koleksi yang mendasarinya, Anda cukup mendapatkan yang baru.

Tentu saja Anda bisa melakukannya dengan ForEach(yang bukan LINQ, tetapi, ekstensi pada List<T>). Tapi ini benar-benar menggunakan foreach, tetapi dengan ekspresi lambda. Terlepas dari ini, setiap metode LINQ secara internal mengulang koleksi Anda, misalnya dengan menggunakan foreachatau for, namun itu menyembunyikannya dari klien. Saya tidak menganggap ini lebih mudah dibaca atau dipelihara (bayangkan mengedit kode Anda saat debugging metode yang mengandung ekspresi lambda).

Setelah mengatakan ini seharusnya tidak menggunakan LINQ untuk memodifikasi item dalam koleksi Anda. Cara yang lebih baik adalah solusi yang sudah Anda berikan dalam pertanyaan Anda. Dengan loop klasik, Anda dapat dengan mudah mengulang koleksi Anda dan memperbarui item-itemnya. Sebenarnya semua solusi yang diandalkan List.ForEachitu tidak berbeda, tetapi jauh lebih sulit untuk dibaca dari sudut pandang saya.

Jadi Anda tidak boleh menggunakan LINQ dalam kasus-kasus di mana Anda ingin memperbarui elemen koleksi Anda.


3
Di luar topik: Saya setuju, dan ada banyak sekali contoh LINQ yang disalahgunakan, contoh orang yang meminta "rantai LINQ kinerja tinggi", untuk melakukan apa yang dapat dicapai dengan satu loop, dll. Saya bersyukur bahwa TIDAK menggunakan LINQ adalah terlalu melekat pada saya, dan biasanya tidak menggunakannya. Saya melihat orang-orang menggunakan rantai LINQ untuk melakukan satu tindakan, tidak menyadari bahwa hampir setiap kali perintah LINQ digunakan Anda membuat forloop lain "di bawah tenda". Saya merasa ini adalah sintaksis gula untuk menciptakan cara-cara sederhana dalam melakukan tugas-tugas sederhana, bukan untuk menjadi pengganti pengkodean standar.
SelamanyaZ

6

Saya sudah mencoba beberapa variasi dalam hal ini, dan saya terus kembali ke solusi orang ini.

http://www.hookedonlinq.com/UpdateOperator.ashx

Sekali lagi, ini adalah solusi orang lain. Tapi saya sudah mengkompilasi kode ke perpustakaan kecil, dan menggunakannya secara teratur.

Saya akan menempelkan kodenya di sini, untuk kemungkinan situsnya (blog) tidak ada lagi di masa mendatang. (Tidak ada yang lebih buruk daripada melihat posting yang mengatakan "Inilah jawaban tepat yang Anda butuhkan", Klik, dan URL Mati.)

    public static class UpdateExtensions {

    public delegate void Func<TArg0>(TArg0 element);

    /// <summary>
    /// Executes an Update statement block on all elements in an IEnumerable<T> sequence.
    /// </summary>
    /// <typeparam name="TSource">The source element type.</typeparam>
    /// <param name="source">The source sequence.</param>
    /// <param name="update">The update statement to execute for each element.</param>
    /// <returns>The numer of records affected.</returns>
    public static int Update<TSource>(this IEnumerable<TSource> source, Func<TSource> update)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (update == null) throw new ArgumentNullException("update");
        if (typeof(TSource).IsValueType)
            throw new NotSupportedException("value type elements are not supported by update.");

        int count = 0;
        foreach (TSource element in source)
        {
            update(element);
            count++;
        }
        return count;
    }
}



int count = drawingObjects
        .Where(d => d.IsSelected && d.Color == Colors.Blue)
        .Update(e => { e.Color = Color.Red; e.Selected = false; } );

1
Anda dapat menggunakan Action<TSource>alih - alih membuat delegasi tambahan. Itu mungkin belum tersedia pada saat penulisan itu.
Frank J

Ya, itu adalah sekolah tua DotNet pada saat itu. Komentar bagus, Frank.
granadaCoder

URL sudah mati! (Nama Domain Ini Telah Kedaluwarsa) Untung saya menyalin kode di sini! #patOnShoulder
granadaCoder

1
Memeriksa tipe nilai masuk akal, tetapi mungkin akan lebih baik menggunakan batasan, yaitu where T: structuntuk menangkap ini pada waktu kompilasi.
Groo


3

Saya menulis beberapa metode ekstensi untuk membantu saya.

namespace System.Linq
{
    /// <summary>
    /// Class to hold extension methods to Linq.
    /// </summary>
    public static class LinqExtensions
    {
        /// <summary>
        /// Changes all elements of IEnumerable by the change function
        /// </summary>
        /// <param name="enumerable">The enumerable where you want to change stuff</param>
        /// <param name="change">The way you want to change the stuff</param>
        /// <returns>An IEnumerable with all changes applied</returns>
        public static IEnumerable<T> Change<T>(this IEnumerable<T> enumerable, Func<T, T> change  )
        {
            ArgumentCheck.IsNullorWhiteSpace(enumerable, "enumerable");
            ArgumentCheck.IsNullorWhiteSpace(change, "change");

            foreach (var item in enumerable)
            {
                yield return change(item);
            }
        }

        /// <summary>
        /// Changes all elements of IEnumerable by the change function, that fullfill the where function
        /// </summary>
        /// <param name="enumerable">The enumerable where you want to change stuff</param>
        /// <param name="change">The way you want to change the stuff</param>
        /// <param name="where">The function to check where changes should be made</param>
        /// <returns>
        /// An IEnumerable with all changes applied
        /// </returns>
        public static IEnumerable<T> ChangeWhere<T>(this IEnumerable<T> enumerable, 
                                                    Func<T, T> change,
                                                    Func<T, bool> @where)
        {
            ArgumentCheck.IsNullorWhiteSpace(enumerable, "enumerable");
            ArgumentCheck.IsNullorWhiteSpace(change, "change");
            ArgumentCheck.IsNullorWhiteSpace(@where, "where");

            foreach (var item in enumerable)
            {
                if (@where(item))
                {
                    yield return change(item);
                }
                else
                {
                    yield return item;
                }
            }
        }

        /// <summary>
        /// Changes all elements of IEnumerable by the change function that do not fullfill the except function
        /// </summary>
        /// <param name="enumerable">The enumerable where you want to change stuff</param>
        /// <param name="change">The way you want to change the stuff</param>
        /// <param name="where">The function to check where changes should not be made</param>
        /// <returns>
        /// An IEnumerable with all changes applied
        /// </returns>
        public static IEnumerable<T> ChangeExcept<T>(this IEnumerable<T> enumerable,
                                                     Func<T, T> change,
                                                     Func<T, bool> @where)
        {
            ArgumentCheck.IsNullorWhiteSpace(enumerable, "enumerable");
            ArgumentCheck.IsNullorWhiteSpace(change, "change");
            ArgumentCheck.IsNullorWhiteSpace(@where, "where");

            foreach (var item in enumerable)
            {
                if (!@where(item))
                {
                    yield return change(item);
                }
                else
                {
                    yield return item;
                }
            }
        }

        /// <summary>
        /// Update all elements of IEnumerable by the update function (only works with reference types)
        /// </summary>
        /// <param name="enumerable">The enumerable where you want to change stuff</param>
        /// <param name="update">The way you want to change the stuff</param>
        /// <returns>
        /// The same enumerable you passed in
        /// </returns>
        public static IEnumerable<T> Update<T>(this IEnumerable<T> enumerable,
                                               Action<T> update) where T : class
        {
            ArgumentCheck.IsNullorWhiteSpace(enumerable, "enumerable");
            ArgumentCheck.IsNullorWhiteSpace(update, "update");
            foreach (var item in enumerable)
            {
                update(item);
            }
            return enumerable;
        }

        /// <summary>
        /// Update all elements of IEnumerable by the update function (only works with reference types)
        /// where the where function returns true
        /// </summary>
        /// <param name="enumerable">The enumerable where you want to change stuff</param>
        /// <param name="update">The way you want to change the stuff</param>
        /// <param name="where">The function to check where updates should be made</param>
        /// <returns>
        /// The same enumerable you passed in
        /// </returns>
        public static IEnumerable<T> UpdateWhere<T>(this IEnumerable<T> enumerable,
                                               Action<T> update, Func<T, bool> where) where T : class
        {
            ArgumentCheck.IsNullorWhiteSpace(enumerable, "enumerable");
            ArgumentCheck.IsNullorWhiteSpace(update, "update");
            foreach (var item in enumerable)
            {
                if (where(item))
                {
                    update(item);
                }
            }
            return enumerable;
        }

        /// <summary>
        /// Update all elements of IEnumerable by the update function (only works with reference types)
        /// Except the elements from the where function
        /// </summary>
        /// <param name="enumerable">The enumerable where you want to change stuff</param>
        /// <param name="update">The way you want to change the stuff</param>
        /// <param name="where">The function to check where changes should not be made</param>
        /// <returns>
        /// The same enumerable you passed in
        /// </returns>
        public static IEnumerable<T> UpdateExcept<T>(this IEnumerable<T> enumerable,
                                               Action<T> update, Func<T, bool> where) where T : class
        {
            ArgumentCheck.IsNullorWhiteSpace(enumerable, "enumerable");
            ArgumentCheck.IsNullorWhiteSpace(update, "update");

            foreach (var item in enumerable)
            {
                if (!where(item))
                {
                    update(item);
                }
            }
            return enumerable;
        }
    }
}

Saya menggunakannya seperti ini:

        List<int> exampleList = new List<int>()
            {
                1, 2 , 3
            };

        //2 , 3 , 4
        var updated1 = exampleList.Change(x => x + 1);

        //10, 2, 3
        var updated2 = exampleList
            .ChangeWhere(   changeItem => changeItem * 10,          // change you want to make
                            conditionItem => conditionItem < 2);    // where you want to make the change

        //1, 0, 0
        var updated3 = exampleList
            .ChangeExcept(changeItem => 0,                          //Change elements to 0
                          conditionItem => conditionItem == 1);     //everywhere but where element is 1

Untuk referensi cek argumen:

/// <summary>
/// Class for doing argument checks
/// </summary>
public static class ArgumentCheck
{


    /// <summary>
    /// Checks if a value is string or any other object if it is string
    /// it checks for nullorwhitespace otherwhise it checks for null only
    /// </summary>
    /// <typeparam name="T">Type of the item you want to check</typeparam>
    /// <param name="item">The item you want to check</param>
    /// <param name="nameOfTheArgument">Name of the argument</param>
    public static void IsNullorWhiteSpace<T>(T item, string nameOfTheArgument = "")
    {

        Type type = typeof(T);
        if (type == typeof(string) ||
            type == typeof(String))
        {
            if (string.IsNullOrWhiteSpace(item as string))
            {
                throw new ArgumentException(nameOfTheArgument + " is null or Whitespace");
            }
        }
        else
        {
            if (item == null)
            {
                throw new ArgumentException(nameOfTheArgument + " is null");
            }
        }

    }
}

2

2 sen saya: -

 collection.Count(v => (v.PropertyToUpdate = newValue) == null);

7
Saya suka pemikirannya, tetapi tidak begitu jelas apa yang dilakukan oleh kode
lomaxx

2

Anda bisa menggunakan LINQ untuk mengonversi koleksi Anda menjadi sebuah array dan kemudian memanggil Array.ForEach ():

Array.ForEach(MyCollection.ToArray(), item=>item.DoSomeStuff());

Jelas ini tidak akan berfungsi dengan koleksi struct atau tipe inbuilt seperti integer atau string.


1

Berikut adalah metode ekstensi yang saya gunakan ...

    /// <summary>
    /// Executes an Update statement block on all elements in an  IEnumerable of T
    /// sequence.
    /// </summary>
    /// <typeparam name="TSource">The source element type.</typeparam>
    /// <param name="source">The source sequence.</param>
    /// <param name="action">The action method to execute for each element.</param>
    /// <returns>The number of records affected.</returns>
    public static int Update<TSource>(this IEnumerable<TSource> source, Func<TSource> action)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (action == null) throw new ArgumentNullException("action");
        if (typeof (TSource).IsValueType)
            throw new NotSupportedException("value type elements are not supported by update.");

        var count = 0;
        foreach (var element in source)
        {
            action(element);
            count++;
        }
        return count;
    }

Mengapa "elemen tipe nilai tidak didukung oleh pembaruan" ?? Tidak ada yang mengganggu itu!
abatishchev

Itu khusus untuk proyek yang sedang saya kerjakan. Saya kira itu tidak masalah dalam banyak kasus. Akhir-akhir ini saya telah mengolahnya dan menamainya menjadi Jalankan (...), menghapus hal jenis nilai dan mengubahnya untuk kembali batal dan menjatuhkan kode hitung.
Bill Forney

Itu kurang lebih apa List<T>.ForEachjuga, tetapi hanya untuk semua IEnumerable.
HimBromBeere

Melihat kembali ini sekarang saya akan mengatakan hanya menggunakan loop foreach. Satu-satunya manfaat menggunakan sesuatu seperti ini adalah jika Anda ingin rantai metode bersama dan mengembalikan enumerable dari fungsi untuk melanjutkan rantai setelah menjalankan tindakan. Kalau tidak, ini hanya panggilan metode tambahan tanpa manfaat.
Bill Forney

0

Saya berasumsi Anda ingin mengubah nilai di dalam kueri sehingga Anda bisa menulis fungsi untuk itu

void DoStuff()
{
    Func<string, Foo, bool> test = (y, x) => { x.Bar = y; return true; };
    List<Foo> mylist = new List<Foo>();
    var v = from x in mylist
            where test("value", x)
            select x;
}

class Foo
{
    string Bar { get; set; }
}

Tetapi jangan shure jika ini yang Anda maksud.


Ini akan menjadi semacam arah yang benar dengan akan memerlukan sesuatu untuk menghitung v, kalau tidak akan melakukan apa-apa.
AnthonyWJones


-3

Misalkan kita memiliki data seperti di bawah ini,

var items = new List<string>({"123", "456", "789"});
// Like 123 value get updated to 123ABC ..

dan jika kita ingin memodifikasi daftar dan mengganti nilai daftar yang ada dengan nilai yang dimodifikasi, maka pertama-tama buat daftar kosong baru, kemudian lewati daftar data dengan menerapkan metode modifikasi pada setiap item daftar,

var modifiedItemsList = new List<string>();

items.ForEach(i => {
  var modifiedValue = ModifyingMethod(i);
  modifiedItemsList.Add(items.AsEnumerable().Where(w => w == i).Select(x => modifiedValue).ToList().FirstOrDefault()?.ToString()) 
});
// assign back the modified list
items = modifiedItemsList;

2
Mengapa Anda membuat sesuatu yang dapat berjalan di O (n) runtime dieksekusi di O (n ^ 2) atau lebih buruk? Saya tidak tahu bagaimana spesifikasi LINQ bekerja tetapi saya dapat melihat ini adalah minimum ^ 2 solusi untuk masalah n .
Fallenreaper
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.