سؤال

النظر في هذا:

var me = new { FirstName = "John", LastName = "Smith" };

هذا جيد كما يمكننا القيام بذلك:

Console.WriteLine("{0} {1}", me.FirstName, me.LastName);

ومع ذلك لا يمكننا القيام بذلك:

public T GetMe()
{
    return new { FirstName = "John", LastName = "Smith" };
}

لأننا لا نعرف نوع T.

يمكننا أن نفعل هذا:

public object GetMe()
{
    return new { FirstName = "John", LastName = "Smith" };
}

ولكن بعد ذلك ، يتعين علينا فحص خصائص الكائن باستخدام الانعكاس من أجل الوصول إليها:

var p = new Prog();
object o = p.GetMe();
Type t = o.GetType();
foreach (var prop in t.GetProperties())
{
    Console.WriteLine(prop.Name + ": " + prop.GetValue(o, null));
}

ولكن ماذا لو استطعنا تسمية نوع مجهول كما نحدده؟ بالطبع لن يكون مجهول الهوية ، ومع ذلك سيكون أكثر إيجازًا وقابلًا للصيانة من تعريف الطبقة العادية.

النظر في هذا:

public Person GetMe()
{
    return new public class Person { FirstName = "John", LastName = "Smith" };
}

الفائدة هي أنه من الممكن عندئذ إعادة نتيجة استعلام LINQ المعقد من طريقة دون الحاجة إلى تحديد الفئة بشكل صريح.

النظر في هذا الاستعلام المعقد نسبيا LINQ:

List<int> list = new List<int>();
var query = from number in list
            select
                new
                    {
                        Number = number,
                        Square = number*number,
                        Absolute = Math.Abs(number),
                        Range = Enumerable.Range(0, number)
                    };

بدلاً من تحديد فصل مثل ذلك:

public class MyNumbers
{
    public int Number { get; set; }
    public int Square { get; set; }
    public int Absolute { get; set; }
    public IEnumerable<int> Range { get; set; }
}

من أجل إعادة متغير الاستعلام من طريقة يمكننا بدلاً من ذلك القيام بذلك:

List<int> list = new List<int>();
return from number in list
            select new public class MyNumbers
                    {
                        Number = number,
                        Square = number*number,
                        Absolute = Math.Abs(number),
                        Range = Enumerable.Range(0, number)
                    };
هل كانت مفيدة؟

المحلول

في الواقع ، هناك "اختراق" يمكنك القيام به للحصول على نوع مجهول من طريقة ما. النظر في هذا:

public object MyMethod()
    {
        var myNewObject = new
        {
            stringProperty = "Hello, World!",
            intProperty = 1337,
            boolProperty = false
        };

        return myNewObject;
    }

    public T Cast<T>(object obj, T type)
    {
        return (T)obj;
    }

يمكنك الآن القيام بذلك:

var obj = MyMethod();
var myNewObj = Cast(obj, new { stringProperty = "", intProperty = 0, boolProperty = false });

سيكون MyNewobj الآن كائنًا من نفس النوع مثل النوع المجهول.

نصائح أخرى

ميزة اللغة التي تحتاجها هي:

public var GetMe()
{
    return new { FirstName = "John", LastName = "Smith" };
}

هذا هو، var سيكون صالحًا كنوع إرجاع الطريقة ، وسيستنتج المترجم النوع الفعلي من أي شيء يتم إرجاعه. ثم يجب عليك القيام بذلك في موقع الاتصال:

var me = GetMe();

سيكون أي نوعان مجهولان مع أعضاء من نفس النوع هو نفس النوع ، لذلك إذا كتبت وظائف أخرى تعيد نفس النمط ، فسيكون لديهم نفس النوع. بالنسبة لأي أنواع A و B حيث تحتوي B على مجموعة فرعية من أعضاء A ، فإن A متوافق مع المهمة مع B (B يشبه فئة أساسية من A). إذا كتبت:

public var GetMeFrom(var names)
{
    return new { FirstName = names["First"], LastName = names["Last"] };
}

سيحدد المترجم بشكل فعال هذا على أنه طريقة عامة مع معلمتين من النوع ، T1 كونه نوع الأسماء و T2 كونه النوع الذي تم إرجاعه من قبل الفهرس T1 هذا يقبل سلسلة. T1 سيكون مقيدًا بحيث يكون لديه فهرس يقبل سلسلة. وفي موقع المكالمات ، يمكنك فقط تمرير أي شيء لديه فهرس قبل سلسلة وأعاد أي نوع تحب, وهذا من شأنه أن يحدد نوع FirstName و LastName في النوع الذي تم إرجاعه بواسطة GetMeFrom.

لذا ، من شأن الاستدلال أن يكتشف كل هذا بالنسبة لك ، ويلتقط تلقائيًا أي قيود نوع يمكن اكتشافها من الكود.

IMHO المشكلة الجذرية لا علاقة لها بالأنواع المجهولة ، لكن إعلان الفصل هو مطوّل للغاية.

الخيار 1:

إذا أمكنك إعلان فصل مثل هذا:

public class MyClass
{ properties={ int Number, int Square, int Absolute, IEnumerable<int> Range } }

أو بطريقة أخرى سريعة بالمثل (مثل مثال Tuple) ، فلن تشعر بالحاجة إلى القيام بأشياء اختراق مع أنواع مجهولة فقط لحفظ بعض التعليمات البرمجية.

عندما يصل "برنامج التحويل البرمجي كخدمة" إلى C#5 ، نأمل أن يقوموا بعمل جيد في دمجها وسنكون قادرين على استخدام metaprogramming لحل هذه الأنواع من المشاكل بشكل نظيف. حفلة مثل عام 1958!

الخيار 2:

بدلاً من ذلك ، في C#4 ، يمكنك فقط تمرير نوع مجهول حوله dynamic وتجنب كل الصب. بالطبع هذا يفتح لك أخطاء وقت التشغيل إذا قمت بإعادة تسمية متغير ، إلخ.

الخيار 3:

إذا كان C# سوف يقوم بتنفيذ الأداء الوطني بنفس طريقة C ++ ، فيمكنك نقل النوع المجهول إلى طريقة ، وطالما كان لديه الأعضاء المناسبين ، فسيتم تجميعه فقط. ستحصل على جميع فوائد السلامة الثابتة ، ولا يوجد أي من الجوانب السلبية. في كل مرة يجب أن أكتب where T : ISomething في C# أشعر بالضيق لأنهم لم يفعلوا هذا!

ما تصفه (الأنواع المجهولة المسماة) هو "أنواع tuple" في الأساس.

أعتقد أنهم سيكونون إضافة لطيفة إلى C#.

إذا كنت أقوم بتصميم مثل هذه الميزة لـ C#، فسأفضّلها باستخدام بناء الجملة مثل هذا:

tuple<int x, int y>

حتى تتمكن من فعل:

public tuple<int x, int y> GetStuff()
{
}

أود بعد ذلك تغيير تعريف الأنواع المجهولة ، بحيث:

new { x = 2, y = 2}

كان tuple<int x, int y> كما هو نوع ، وليس نوع مجهول.

يعد الحصول على هذا العمل مع CLR الحالي أمرًا صعبًا بعض الشيء ، لأنه بمجرد أن تتمكن من تسمية نوع مجهول في التوقيعات العامة ، يجب أن تكون قادرًا على توحيدها عبر التجميعات المترجمة بشكل منفصل. يمكن إنجازه عن طريق تضمين "مُنشئ الوحدة" داخل أي مجموعة تستخدم نوعًا من النقل. نرى هذا المشنور على سبيل المثال.

الجانب السلبي الوحيد لهذا النهج هو أنه لا يحترم النموذج "الكسول" لـ CLR لتوليد النوع. هذا يعني أن التجميعات التي تستخدم العديد من أنواع tuple المتميزة قد تواجه أنواع الحمل أبطأ قليلاً. سيكون النهج الأفضل هو إضافة دعم لأنواع tuple مباشرة إلى CLR.

ولكن ، بصرف النظر عن تغيير CLR ، أعتقد أن نهج مُنشئ الوحدة هو أفضل طريقة لفعل شيء كهذا.

أحب هذه الميزة ، كانت هناك عدة مرات أردت ذلك.

مثال جيد هو معالجة XML. يمكنك تحليلهم استعادة كائن ، ولكن بعد ذلك تحتاج إلى إنشاء نسخة ملموسة من الكائن لإعادة إرسالها إلى المتصل. في كثير من الأحيان تحصل على XML التي تتغير إلى حد كبير وتتطلب منك جعل العديد من الفصول للتعامل معها. ألن يكون الأمر رائعًا إذا تمكنت فقط من إنشاء الكائن باستخدام linqtoxml كـ var ، فما عليك سوى إرجاع ذلك؟

أعتقد أن هذا سيكون سحر برمجي لطيف ل tuples:

إنشاء توبلي:

(int, string, Person) tuple = (8, "hello", new Person());

أي ما يعادل:

Tuple<int,string,Person> tuple = new Tuple<int,string,Person>(8 ,"hello", new Person());

في وظيفة:

public (int, string, Person) GetTuple(){
    return ...
}

الحصول على القيم:

int number = tuple[1];
string text = tuple[2];
Person person = tuple[3];

هل يمكنك إنشاء واجهة مع FirstName FirstName و LastName واستخدام ذلك؟

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