سؤال

أقوم بتجربة الأدوية العامة وأحاول إنشاء بنية مشابهة لفئة Dataset.
لدي الكود التالي

public struct Column<T>
{
    T value;
    T originalValue;

    public bool HasChanges
    {
        get { return !value.Equals(originalValue); }
    }

    public void AcceptChanges()
    {
        originalValue = value;
    }
}

public class Record
{
    Column<int> id;
    Column<string> name;
    Column<DateTime?> someDate;
    Column<int?> someInt;

    public bool HasChanges
    {
        get
        {
            return id.HasChanges | name.HasChanges | someDate.HasChanges | someInt.HasChanges;
        }
    }

    public void AcceptChanges()
    {
        id.AcceptChanges();
        name.AcceptChanges();
        someDate.AcceptChanges();
        someInt.AcceptChanges();
    }
}

المشكلة التي أواجهها هي أنه عندما أقوم بإضافة عمود جديد أحتاج إلى إضافته أيضًا في خاصية HasChanges وطريقة AcceptChanges().هذا يتطلب فقط بعض إعادة الهيكلة.
لذا فإن الحل الأول الذي يتبادر إلى ذهني هو شيء من هذا القبيل:

public interface IColumn
{
    bool HasChanges { get; }
    void AcceptChanges();
}

public struct Column<T> : IColumn {...}
public class Record
{
    Column<int> id;
    Column<string> name;
    Column<DateTime?> someDate;
    Column<int?> someInt;

    IColumn[] Columns { get { return new IColumn[] {id, name, someDate, someInt}; }}

    public bool HasChanges
    {
        get
        {
            bool has = false;
            IColumn[] columns = Columns;            //clone and boxing
            for (int i = 0; i < columns.Length; i++)
                has |= columns[i].HasChanges;
            return has;
        }
    }

    public void AcceptChanges()
    {
        IColumn[] columns = Columns;            //clone and boxing
        for (int i = 0; i < columns.Length; i++)
            columns[i].AcceptChanges();         //Here we are changing clone
    }
}

كما ترون من التعليقات لدينا مشاكل قليلة هنا مع استنساخ البنية.الحل البسيط لذلك هو تغيير العمود إلى فئة، ولكن من اختباراتي يبدو أنه يزيد من استخدام الذاكرة بنسبة 40٪ تقريبًا (بسبب البيانات التعريفية لكل كائن) وهو أمر غير مقبول بالنسبة لي.

لذلك سؤالي هو:هل لدى أي شخص أي أفكار أخرى حول كيفية إنشاء أساليب يمكنها العمل على كائنات/سجلات منظمة مختلفة؟ربما يمكن لشخص من مجتمع F# أن يقترح كيفية حل هذه المشكلات في اللغات الوظيفية وكيف تؤثر على الأداء واستخدام الذاكرة.

يحرر:
sfg شكرًا لاقتراحك بشأن وحدات الماكرو.
يوجد في Visual Studio 2008 محرك قالب مدمج (لكنه غير معروف) يسمى T4.بيت القصيد هو إضافة ملف ".tt" إلى مشروعي وإنشاء قالب يبحث في جميع فصولي، ويتعرف بطريقة ما على السجلات (على سبيل المثال من خلال بعض الواجهات التي ينفذونها) وينتج فئات جزئية باستخدام HasChanges وAcceptChanges( ) والتي ستستدعي فقط الأعمدة التي يحتوي عليها الفصل.

بعض الروابط المفيدة:
محرر T4 لـ VS
مدونة تحتوي على روابط ودروس تعليمية حول T4
إدخال مدونة مع مثال يستخدم EnvDTE لقراءة ملفات المشروع

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

المحلول

كما طلبت أمثلة من اللغات الوظيفية؛في lisp، يمكنك منع كتابة كل هذه التعليمات البرمجية عند كل إضافة لعمود باستخدام ماكرو لإخراج التعليمات البرمجية لك.للأسف، لا أعتقد أن هذا ممكن في C#.

من حيث الأداء:سيتم تقييم الماكرو في وقت الترجمة (وبالتالي إبطاء الترجمة بمقدار ضئيل)، ولكنه لن يسبب أي تباطؤ في وقت التشغيل لأن كود وقت التشغيل سيكون هو نفسه ما ستكتبه يدويًا.

أعتقد أنه قد يتعين عليك قبول AcceptChanges() الأصلية الخاصة بك لأنك تحتاج إلى الوصول إلى البنيات مباشرة من خلال معرفاتها إذا كنت تريد تجنب الكتابة إلى الإصدارات المستنسخة.

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

نصائح أخرى

يمكنك استخدام الانعكاس للتكرار على الأعضاء واستدعاء HasChanges وAcceptChanges.يمكن لفئة السجل تخزين بيانات تعريف الانعكاس كبيانات ثابتة، لذلك لا يوجد حمل للذاكرة لكل مثيل.ومع ذلك، فإن تكلفة الأداء في وقت التشغيل ستكون ضخمة - قد ينتهي بك الأمر أيضًا إلى وضع العمود في المربع وإلغاء تحديده مما قد يضيف المزيد إلى التكلفة.

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

لا يمكنك تخزين أعمدتك بكفاءة في مجموعة من IColumns، لذلك لن يعمل أي نهج صفيف بشكل جيد.ليس لدى المترجم طريقة لمعرفة أن ملف IColumn ستحتفظ المصفوفة بالبنيات فقط، وفي الواقع، لن يكون الأمر مفيدًا إذا فعلت ذلك، لأنه لا تزال هناك أنواع مختلفة من البنيات التي تحاول تشويشها هناك.في كل مرة يتصل شخص ما AcceptChanges() أو HasChanges(), ، سوف ينتهي بك الأمر إلى الملاكمة واستنساخ بنياتك على أي حال، لذلك أشك بشدة في أن صنع Column سيوفر لك الهيكل بدلاً من الفصل الدراسي الكثير من الذاكرة.

ولكن هل استطاع ربما تخزين الخاص بك Columns مباشرة في مصفوفة وفهرستها باستخدام التعداد.على سبيل المثال:

public class Record
{
    public enum ColumnNames { ID = 0, Name, Date, Int, NumCols };

    private IColumn [] columns;

    public Record()
    {
        columns = new IColumn[ColumnNames.NumCols];
        columns[ID] = ...
    }

    public bool HasChanges
    {
        get
        {
            bool has = false;
            for (int i = 0; i < columns.Length; i++)
                has |= columns[i].HasChanges;
            return has;
        }
    }

    public void AcceptChanges()
    {
        for (int i = 0; i < columns.Length; i++)
            columns[i].AcceptChanges();
    }
}

ليس لدي مترجم C# في متناول يدي، لذلك لا يمكنني التحقق لمعرفة ما إذا كان ذلك سيعمل أم لا، ولكن الفكرة الأساسية يجب أن تعمل، حتى لو لم أحصل على كل التفاصيل بشكل صحيح.ومع ذلك، سأمضي قدمًا وأجعلهم فصولًا دراسية.أنت تدفع ثمنها على أي حال.

الطريقة الوحيدة التي يمكنني التفكير بها لفعل ما تريد فعله حقًا هي استخدام الانعكاس.سيظل هذا في وضع الصندوق/الفتح، لكنه كان تسمح لك بتخزين النسخة مرة أخرى في الحقل، مما يجعلها القيمة الحقيقية بشكل فعال.

public void AcceptChanges()
{
    foreach (FieldInfo field in GetType().GetFields()) {
        if (!typeof(IColumn).IsAssignableFrom(field.FieldType))
            continue; // ignore all non-IColumn fields
        IColumn col = (IColumn)field.GetValue(this); // Boxes storage -> clone
        col.AcceptChanges(); // Works on clone
        field.SetValue(this, col); // Unboxes clone -> storage
    }
}

وماذا عن هذا:

public interface IColumn<T>
{
    T Value { get; set; }
    T OriginalValue { get; set; }
}

public struct Column<T> : IColumn<T>
{
    public T Value { get; set; }
    public T OriginalValue { get; set; }
}

public static class ColumnService
{
    public static bool HasChanges<T, S>(T column) where T : IColumn<S>
    {
        return !(column.Value.Equals(column.OriginalValue));
    }

    public static void AcceptChanges<T, S>(T column) where T : IColumn<S>
    {
        column.Value = column.OriginalValue;
    }
}

رمز العميل هو بعد ذلك:

Column<int> age = new Column<int>();
age.Value = 35;
age.OriginalValue = 34;

if (ColumnService.HasChanges<Column<int>, int>(age))
{
    ColumnService.AcceptChanges<Column<int>, int>(age);
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top