سؤال

لدي واجهة محددة على النحو التالي:

public interface TestInterface{
    int id { get; set; }
}

وهناك فئتان من Linq-to-SQL تنفذان تلك الواجهة:

public class tblTestA : TestInterface{
    public int id { get; set; }
}

public class tblTestB : TestInterface{
    public int id { get; set; }
}

لدي قوائم IEnumerable a وb مملوءة بسجلات قاعدة البيانات من tblTestA وtblTestB

IEnumerable<tblTestA> a = db.tblTestAs.AsEnumerable();
IEnumerable<tblTestB> b = db.tblTestBs.AsEnumerable();

ومع ذلك، لا يجوز ما يلي:

List<TestInterface> list = new List<TestInterface>();
list.AddRange(a);
list.AddRange(b);

يجب أن أفعل كما يلي:

foreach(tblTestA item in a)
    list.Add(item)

foreach(tblTestB item in b)
    list.Add(item)

هل هناك شيء أفعله خطأ؟شكرا على اي مساعدة

هل كانت مفيدة؟

المحلول

هذا يعمل في C# 4، بسبب التباين المشترك.على عكس الإصدارات السابقة من C#، هناك تحويل من IEnumerable<tblTestA> ل IEnumerable<TestInterface>.

كانت الوظيفة موجودة في CLR منذ الإصدار 2، ولكن تم الكشف عنها فقط في C# 4 (وأنواع إطار العمل لم تستفد منها قبل .NET 4 أيضًا).هو - هي فقط ينطبق على الواجهات العامة والمفوضين (وليس الفئات) وفقط لأنواع المراجع (لذلك لا يوجد تحويل من IEnumerable<int> ل IEnumerable<object> على سبيل المثال.) كما أنه يعمل فقط عندما يكون ذلك منطقيًا - IEnumerable<T> متغير لأن الكائنات تخرج فقط من واجهة برمجة التطبيقات (API)، بينما IList<T> يكون ثابت لأنه يمكنك إضافة قيم باستخدام واجهة برمجة التطبيقات هذه أيضًا.

يتم أيضًا دعم التباين العام، حيث يعمل في الاتجاه الآخر - لذلك على سبيل المثال يمكنك التحويل من IComparer<object> ل IComparer<string>.

إذا كنت لا تستخدم C# 4، فاقتراح تيم باستخدامه Enumerable.Cast<T> إنها فكرة جيدة - ستفقد القليل من الكفاءة، لكنها ستنجح.

إذا كنت تريد معرفة المزيد عن التباين العام، فلديك إريك ليبرت سلسلة طويلة من منشورات المدونة حول هذا الموضوع, ، وقد ألقيت محاضرة حول هذا الموضوع في NDC 2010 والتي يمكنك مشاهدتها على صفحة فيديو NDC.

نصائح أخرى

أنت لا تفعل أي شيء خاطئ: List<TestInterface>.AddRange يتوقع ان IEnumerable<TestInterface>.لن يقبل أ IEnumerable<tblTestA> أو IEnumerable<tblTestB>.

لك foreach عمل الحلقات.بدلا من ذلك، يمكنك استخدام Cast لتغيير الأنواع:

List<TestInterface> list = new List<TestInterface>();
list.AddRange(a.Cast<TestInterface>());
list.AddRange(b.Cast<TestInterface>());

يتوقع Addrange قائمة كائنات الواجهة، ويتم تعريف Varibles الخاص بك "A" و "B" لتكون قائمة من كائنات الفئة المشتقة.من الواضح أنه يبدو من المعقول أن .NET قد يجعل من المنطق المنطقي أن تقفزها ونتعامل معها كقوائم من كائنات الواجهة لأنها تقوم بالفعل بتنفيذ الواجهة، وكان هذا المنطق لا يبني فقط إلى 3.5.

ومع ذلك، تمت إضافة هذه القدرة (تسمى "Coviarice") إلى .NET 4.0، ولكن حتى تقوم بالترقية إلى ذلك، ستكون عالقا مع حلقات، أو ربما حاول الاتصال بالتوزيع () ثم يلقي النتيجة إلىtaskinterface []، أو ربما استعلام LINQ لحالة كل عنصر وإنشاء قائمة جديدة، إلخ.

a و b هي من النوع IEnumerable<tblTestA> و IEnumerable<tblTestB>
بينما list.AddRange تتطلب أن تكون المعلمة من النوع IEnumerable<TestInterface>

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top