سؤال

دعنا نقول أن لدي كائن بيانات ، ولكن يمكن لهذا الكائن الاحتفاظ بأحد أنواع البيانات العديدة.

class Foo
{
    int intFoo;
    double doubleFoo;
    string stringFoo;
}

الآن ، أريد إنشاء ملحق. بطريقة ما للحصول على هذه البيانات. من الواضح أنه يمكنني إنشاء ملحقات متعددة:

public int GetIntFoo();
public double GetDoubleFoo();
public string GetStringFoo();

أو يمكنني إنشاء خصائص متعددة

public int IntFoo { get; set; }
public double DoubleFoo { get; set; }
public string StringFoo { get; set; }

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

خيار واحد هو استخدام الأدوية الجيلية.

class Foo<T>
{
    public T TheFoo { get; set; }
}

ومع ذلك ، هذا لا يخلق فو ، فهو يخلق فوu003CT> . نوع مختلف لكل منهما ، لذلك لا يمكنني استخدامها بنفس النوع.

يمكنني استخلاص فوu003CT> من Foobase ، ثم تعامل معهم جميعًا على أنه Foobase ، ولكن بعد ذلك سأعود إلى مشكلة الوصول إلى البيانات.

خيار الأدوية المختلفة هو استخدام شيء مثل هذا:

class Foo
{
    string stringRepresentationOfFoo;
    public T GetFoo<T>() { return /* code to convert string to type */ }
}

بالطبع المشكلة هي أنه يمكن تمرير أي نوع من t ، وبصراحة ، إنه مشغول بعض الشيء.

يمكنني أيضًا مربع القيم وإرجاع كائن ، ولكن بعد ذلك لا يوجد أي نوع من الأمان.

من الناحية المثالية ، أريد أن أتعامل مع كل ما هو متماثل ، لكني أريد نوع السلامة حتى إذا لم يكن هناك stringfoo ، فلا يمكنني حتى تجميع إشارة إلى stringfoo.

Foo foo = new Foo("Foo");
string sFoo = foo.Value;  // succeeds.
&nbsp;
Foo foo = new Foo(0);
int iFoo = foo.Value; // succeeds
string sFoo = foo.Value; // compile error

ربما هذا غير ممكن .. وسأضطر إلى تقديم بعض التسويات ، لكن ربما أفتقد شيئًا.

أيه أفكار؟

تعديل:

حسنًا ، كما يشير دانيال ، فإن وقت الترجمة من نوع وقت التشغيل ليس عمليًا.

ما هو أفضل خياري لفعل ما أريد أن أفعله هنا؟ وهي تعامل مع جميع فو ، ولكن لا يزال لديك آلية وصول عاقلة نسبيا؟

EDIT2:

لا أريد تحويل القيمة إلى أنواع مختلفة. أريد إرجاع النوع الصحيح للقيمة. هذا ، إذا كان مزدوجًا ، فأنا لا أريد إعادة int.

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

المحلول

ماذا عن تمرير المتغير كمعلمة إلى الحصول؟ مثله:

int i = foo.get(i);

ثم في فصلك ، سيكون لديك شيء مثل:

public int get(int p) {
    if(this.type != INTEGER) throw new RuntimeException("Data type mismatch");
    return this.intVal;
}

public float get(float p) {
    if(this.type != FLOAT) throw new RuntimeException("Data type mismatch");
    return this.floatVal;
}

هذا النوع من الدوران يحول النوع الذي يقوم بالتحقق من الداخل إلى الخارج: بدلاً من التحقق من نوع Foo الذي يحمله Foo ، لديك Foo تحقق من النوع الذي تريده. إذا كان يمكن أن يعطيك هذا النوع ، فهو يفعل ، وإلا فإنه يلقي استثناء وقت التشغيل.

نصائح أخرى

لا أعتقد أن هذا يمكن أن ينجح (يمنحك خطأ المترجم الذي تريده)

ماذا تريد أن يفعل هذا:

Foo bar = (new Random()).Next(2) == 0 ? new Foo("bar") : new Foo(1);
int baz = bar.Value;

هل هذا خطأ في المترجم؟

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

على أي حال ، أعتقد أن "أفضل طريقة" ستكون حل وسط بين الأدوية والميراث. يمكنك تحديد أ Foo<T> هذه فئة فرعية من فو. ثم لا يزال بإمكانك الحصول على مجموعات من Foo.

abstract public class Foo
{
  // Common implementation

  abstract public object ObjectValue { get; }
}

public class Foo<T> : Foo
{
   public Foo(T initialValue)
   {
      Value = initialValue;
   }

   public T Value { get; set; }

   public object ObjectValue
   { 
      get { return Value; }
   }
}

تستخدم العديد من الأنظمة طرقًا مساعد لإرجاع الأنواع البديلة تمامًا مثل كائن قاعدة أطر عمل .NET لديه طريقة TOSTRING ()

اختر ما هو أفضل نوع أساسي لكل من كائنك وتوفير طرق لحالات أخرى

على سبيل المثال

class Foo{
    public Int32 Value { get; set; }
    public Byte ToByte() { return Convert.ToByte(Value); }
    public Double ToDouble() { return (Double)Value; }
    public new String ToString() { return Value.ToString("#,###"); }
}

شيء واحد هو تخزين أي نوع في حالتك الداخلية للفصل ، والآخر هو فضحه خارجيًا. عندما تكتب فصلًا ما ، فإنك تعلن بالفعل عقدًا لسلوكه. ستؤثر الطريقة التي تكتب بها بشكل كبير بكيفية ظهور رمز العميل عند استخدام الفصل.

على سبيل المثال ، من خلال تنفيذ iconvertible الواجهة التي تنص على أنه يمكن تحويل نوعك إلى أي نوع CLR كقيمة مكافئة.

لقد رأيت أيضًا تطبيقات حيث تم استخدام فئة القيمة لتخزين نتائج الحسابات ، والنتائج التي يمكن أن تمثل سلسلة أو مزدوجة أو INT أو منطقية. ولكن ، كانت المشكلة هي أن رمز العميل يجب أن يتحقق ) أو استخدم الثمينة المحددة ، القيمة ، valueint ، getters valueBoolean.

لماذا لا تستخدم فقط string, double و int?


بعد معلومات حول المجموعة: ماذا عن استخدام object؟ سيكون عليك التحقق من الأنواع وما بعد ذلك على أي حال. ولمساعدتك في ذلك يمكنك استخدام is و as العاملين. و ال طريقة enumerable.cast, ، أو حتى أفضل ، enumerable.oftype طريقة.

في الواقع ، ما هو الغرض من هذه الفئة؟ يبدو أن المشكلة الأكبر هي تصميم التصميم على الأقل SRP (مبدأ المسؤولية الفردية).

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

مع هذا النهج ، يمكنك استخدام اقتراحك ، أي

namespace Project1 {
public class Class1 {
    static int Main(string[] args) {
        Foo a = new Foo();
        a.SetValue(4);
        Console.WriteLine(a.GetValue<int>());

        Foo b = new Foo();
        a.SetValue("String");
        Console.WriteLine(a.GetValue<string>());

        Console.ReadLine();

        return 0;
    }
}


class Foo {
    private object value; // watch out for boxing here!
    public void SetValue(object value) {
        this.value = value;
    }

    public T GetValue<T>() {
        object val = this.value;
        if (val == null) { return default(T); } // or throw if you prefer
        try {
            return (T)val;
        }
        catch (Exception) {
            return default(T);
            // cast failed, return default(T) or throw
        }
    }
}
}

ومع ذلك ، في هذه الحالة ، لماذا لا تمر فقط البيانات ككائن وتلقي بنفسك؟

بناءً على احتياجاتك ، يمكنك أيضًا تجربة "PHP في C#":

namespace Project1 {
public class Class1 {
    static int Main(string[] args) {
        MyInt a = 1;
        MyInt b = "2";

        Console.WriteLine(a + b); // writes 3

        Console.ReadLine();

        return 0;
    }
}


class MyInt {
    private int value;

    public static implicit operator int(MyInt container) {
        return container.value;
    }

    public static implicit operator MyInt(int value) {
        MyInt myInt = new MyInt();
        myInt.value = value;
        return myInt ;
    }

    public static implicit operator MyInt(string stringedInt) {
        MyInt myInt = new MyInt();
        myInt.value = int.Parse(stringedInt);
        return myInt;
    }
}
}

أنا آسف ، أنا فقط لا أشتري فرضيتك. إذا كانت جميع البيانات لها نفس الغرض ، فينبغي أن يكون لها نفس النوع. فكر في فئة تهدف إلى الاحتفاظ بدرجة الحرارة الحالية ، كما تم إرجاعها من قبل إحدى خدمات الويب العديدة. جميع الخدمات تعيد درجة الحرارة في المركز. ولكن يعود المرء كـ int ، ويعود المرء كمزدوج ، ويعيده المرء كسلسلة.

إنه ليس ثلاثة أنواع مختلفة - إنه نوع واحد - مزدوج. ستحتاج ببساطة إلى تحويل العائدات غير المدونة إلى مزدوجة ، وهو ما تكون درجة الحرارة (أو ربما تعويم).

بشكل عام ، إذا كان لديك تمثيلات متعددة لشيء واحد ، فسيظل شيئًا واحدًا ، وليس أشياء متعددة. تحويل التمثيلات المتعددة إلى واحد.

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