هل هناك قيد يقصر طريقتي العامة على الأنواع الرقمية؟

StackOverflow https://stackoverflow.com/questions/32664

  •  09-06-2019
  •  | 
  •  

سؤال

هل يمكن لأحد أن يخبرني ما إذا كانت هناك طريقة مع الأدوية العامة للحد من وسيطة النوع العام T إلى فقط:

  • Int16
  • Int32
  • Int64
  • UInt16
  • UInt32
  • UInt64

أنا على علم ب where الكلمة الأساسية، ولكن لا يمكن العثور على واجهة لها فقط هذه الأنواع،

شيء مثل:

static bool IntegerFunction<T>(T value) where T : INumeric 
هل كانت مفيدة؟

المحلول

C# لا يدعم هذا.وقد وصف Hejlsberg أسباب عدم تنفيذ هذه الميزة في مقابلة مع بروس إيكل:

وليس من الواضح ما إذا كان التعقيد الإضافي يستحق العائد الصغير الذي تحصل عليه.إذا كان هناك شيء تريد القيام به غير مدعوم بشكل مباشر في نظام القيد، فيمكنك القيام به باستخدام نمط المصنع.يمكن أن يكون لديك Matrix<T>, ، على سبيل المثال، وفي ذلك Matrix كنت ترغب في تحديد طريقة المنتج النقطي.وهذا يعني بالطبع أنك بحاجة في النهاية إلى فهم كيفية ضرب اثنين Ts، لكن لا يمكنك قول ذلك كقيد، على الأقل ليس إذا T يكون int, double, ، أو float.ولكن ما يمكنك فعله هو الحصول على الخاص بك Matrix اتخاذ حجة أ Calculator<T>, ، و في Calculator<T>, ، لديك طريقة تسمى multiply.تذهب لتنفيذ ذلك وتمريره إلى Matrix.

ومع ذلك، يؤدي هذا إلى تعليمات برمجية معقدة إلى حد ما، حيث يتعين على المستخدم توفير تعليمات برمجية خاصة به Calculator<T> التنفيذ لكل T التي يريدون استخدامها.طالما أنه لا يجب أن يكون قابلاً للتوسيع، على سبيل المثال.إذا كنت تريد فقط دعم عدد ثابت من الأنواع، مثل int و double, ، يمكنك التخلص من واجهة بسيطة نسبيًا:

var mat = new Matrix<int>(w, h);

(الحد الأدنى من التنفيذ في GitHub Gist.)

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

var mat = new Matrix<DFP>(DfpCalculator.Instance, w, h);

… وتنفيذ جميع الأعضاء ل DfpCalculator : ICalculator<DFP>.

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

نصائح أخرى

بالنظر إلى شعبية هذا السؤال والاهتمام الكامن وراء مثل هذه الوظيفة، فأنا مندهش عندما أرى أنه لا توجد إجابة تتعلق بـ T4 حتى الآن.

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

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

من أجل القيام بذلك:

  • إنشاء جديد قالب النص ملف يسمى GenericNumberMethodTemplate.tt.
  • قم بإزالة الرمز الذي تم إنشاؤه تلقائيًا (ستحتفظ بمعظمه، ولكن لن تكون هناك حاجة لبعضه).
  • أضف المقتطف التالي:
<#@ template language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>

<# Type[] types = new[] {
    typeof(Int16), typeof(Int32), typeof(Int64),
    typeof(UInt16), typeof(UInt32), typeof(UInt64)
    };
#>

using System;
public static class MaxMath {
    <# foreach (var type in types) { 
    #>
        public static <#= type.Name #> Max (<#= type.Name #> val1, <#= type.Name #> val2) {
            return val1 > val2 ? val1 : val2;
        }
    <#
    } #>
}

هذا كل شيء.لقد انتهيت الآن.

سيؤدي حفظ هذا الملف إلى تجميعه تلقائيًا في هذا الملف المصدر:

using System;
public static class MaxMath {
    public static Int16 Max (Int16 val1, Int16 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static Int32 Max (Int32 val1, Int32 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static Int64 Max (Int64 val1, Int64 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static UInt16 Max (UInt16 val1, UInt16 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static UInt32 Max (UInt32 val1, UInt32 val2) {
        return val1 > val2 ? val1 : val2;
    }
    public static UInt64 Max (UInt64 val1, UInt64 val2) {
        return val1 > val2 ? val1 : val2;
    }
}

في الخاص بك main الطريقة التي يمكنك من خلالها التحقق من أن لديك يقين وقت الترجمة:

namespace TTTTTest
{
    class Program
    {
        static void Main(string[] args)
        {
            long val1 = 5L;
            long val2 = 10L;
            Console.WriteLine(MaxMath.Max(val1, val2));
            Console.Read();
        }
    }
}

enter image description here

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

وهذا ليس هو الحال على الإطلاق هنا:إذا كنت تريد التغيير، فيمكنك فقط تغيير القالب (مصدر واحد لكل جيلك!) وقد تم الأمر.

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

<#@ import namespace="TheNameSpaceYouWillUse" #>
<#@ assembly name="$(TargetPath)" #>

لنكن صادقين:هذا رائع.

تنصل:لقد تأثرت هذه العينة بشدة البرمجة الفوقية في .NET بقلم كيفن هازارد وجيسون بوك، منشورات مانينغ.

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

سأذهب إلى أبعد من ذلك وأقول أننا بحاجة

static bool GenericFunction<T>(T value) 
    where T : operators( +, -, /, * )

او حتى

static bool GenericFunction<T>(T value) 
    where T : Add, Subtract

لسوء الحظ، لديك فقط الواجهات والفئات الأساسية والكلمات الرئيسية struct (يجب أن يكون من نوع القيمة)، class (يجب أن يكون النوع المرجعي) و new() (يجب أن يكون لديك مُنشئ افتراضي)

يمكنك لف الرقم بشيء آخر (على غرار INullable<T>) يحب هنا على مشروع الكود.


يمكنك تطبيق التقييد في وقت التشغيل (من خلال التفكير في عوامل التشغيل أو التحقق من الأنواع) ولكن هذا يفقد ميزة وجود النوع العام في المقام الأول.

الحل البديل باستخدام السياسات:

interface INumericPolicy<T>
{
    T Zero();
    T Add(T a, T b);
    // add more functions here, such as multiplication etc.
}

struct NumericPolicies:
    INumericPolicy<int>,
    INumericPolicy<long>
    // add more INumericPolicy<> for different numeric types.
{
    int INumericPolicy<int>.Zero() { return 0; }
    long INumericPolicy<long>.Zero() { return 0; }
    int INumericPolicy<int>.Add(int a, int b) { return a + b; }
    long INumericPolicy<long>.Add(long a, long b) { return a + b; }
    // implement all functions from INumericPolicy<> interfaces.

    public static NumericPolicies Instance = new NumericPolicies();
}

الخوارزميات:

static class Algorithms
{
    public static T Sum<P, T>(this P p, params T[] a)
        where P: INumericPolicy<T>
    {
        var r = p.Zero();
        foreach(var i in a)
        {
            r = p.Add(r, i);
        }
        return r;
    }

}

الاستخدام:

int i = NumericPolicies.Instance.Sum(1, 2, 3, 4, 5);
long l = NumericPolicies.Instance.Sum(1L, 2, 3, 4, 5);
NumericPolicies.Instance.Sum("www", "") // compile-time error.

الحل آمن في وقت الترجمة. إطار عمل سيتي ليزارد يوفر نسخة مجمعة لـ .NET 4.0.الملف هو lib/NETFramework4.0/CityLizard.Policy.dll.

إنه متوفر أيضًا في Nuget: https://www.nuget.org/packages/CityLizard/.يرى CityLizard.Policy.I بناء.

يعد هذا السؤال جزءًا من الأسئلة الشائعة، لذلك أقوم بنشر هذا على هيئة wiki (بما أنني نشرت شيئًا مشابهًا من قبل، ولكن هذا سؤال أقدم)؛على أي حال...

ما هو إصدار .NET الذي تستخدمه؟إذا كنت تستخدم .NET 3.5، فلدي تنفيذ عوامل التشغيل العامة في MiscUtil (مجاني الخ).

هذا لديه أساليب مثل T Add<T>(T x, T y), ، ومتغيرات أخرى للحساب على أنواع مختلفة (مثل DateTime + TimeSpan).

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

بعض المعلومات الأساسية الإضافية حول سبب صعوبة ذلك هنا.

قد ترغب أيضًا في معرفة ذلك dynamic (4.0) نوعًا ما يحل هذه المشكلة بشكل غير مباشر أيضًا - على سبيل المثال.

dynamic x = ..., y = ...
dynamic result = x + y; // does what you expect

لسوء الحظ، لا يمكنك سوى تحديد البنية في جملة المكان في هذه الحالة.يبدو غريبًا أنك لا تستطيع تحديد Int16 وInt32 وما إلى ذلك.على وجه التحديد ولكني متأكد من أن هناك بعض أسباب التنفيذ العميقة الكامنة وراء قرار عدم السماح بأنواع القيمة في عبارة المكان.

أعتقد أن الحل الوحيد هو إجراء فحص وقت التشغيل والذي للأسف يمنع اكتشاف المشكلة في وقت الترجمة.هذا من شأنه أن يذهب شيء من هذا القبيل: -

static bool IntegerFunction<T>(T value) where T : struct {
  if (typeof(T) != typeof(Int16)  &&
      typeof(T) != typeof(Int32)  &&
      typeof(T) != typeof(Int64)  &&
      typeof(T) != typeof(UInt16) &&
      typeof(T) != typeof(UInt32) &&
      typeof(T) != typeof(UInt64)) {
    throw new ArgumentException(
      string.Format("Type '{0}' is not valid.", typeof(T).ToString()));
  }

  // Rest of code...
}

وأنا أعلم أنه أمر قبيح بعض الشيء، لكنه يوفر على الأقل القيود المطلوبة.

سأبحث أيضًا في الآثار المحتملة على الأداء لهذا التنفيذ، وربما تكون هناك طريقة أسرع.

ربما أقرب ما يمكنك القيام به هو

static bool IntegerFunction<T>(T value) where T: struct

لست متأكدًا مما إذا كان بإمكانك القيام بما يلي

static bool IntegerFunction<T>(T value) where T: struct, IComparable
, IFormattable, IConvertible, IComparable<T>, IEquatable<T>

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

لا توجد طريقة لتقييد القوالب بالأنواع، ولكن يمكنك تحديد إجراءات مختلفة بناءً على النوع.كجزء من حزمة رقمية عامة، كنت بحاجة إلى فئة عامة لإضافة قيمتين.

    class Something<TCell>
    {
        internal static TCell Sum(TCell first, TCell second)
        {
            if (typeof(TCell) == typeof(int))
                return (TCell)((object)(((int)((object)first)) + ((int)((object)second))));

            if (typeof(TCell) == typeof(double))
                return (TCell)((object)(((double)((object)first)) + ((double)((object)second))));

            return second;
        }
    }

لاحظ أنه يتم تقييم الأنواع في وقت الترجمة، لذلك ستتم إزالة عبارات if بواسطة المترجم.يقوم المترجم أيضًا بإزالة القوالب الزائفة.لذلك سيتم حل شيء ما في المترجم إلى

        internal static int Sum(int first, int second)
        {
            return first + second;
        }

لقد قمت بإنشاء وظيفة مكتبة صغيرة لحل هذه المشكلات:

بدلاً من:

public T DifficultCalculation<T>(T a, T b)
{
    T result = a * b + a; // <== WILL NOT COMPILE!
    return result;
}
Console.WriteLine(DifficultCalculation(2, 3)); // Should result in 8.

يمكنك الكتابة:

public T DifficultCalculation<T>(Number<T> a, Number<T> b)
{
    Number<T> result = a * b + a;
    return (T)result;
}
Console.WriteLine(DifficultCalculation(2, 3)); // Results in 8.

يمكنك العثور على الكود المصدري هنا: https://codereview.stackexchange.com/questions/26022/improvement-requested-for-generic-calculator-and-generic-number

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

إذا كان كل ما تريده هو الأعداد الصحيحة، فلا تستخدم عامًا، فهذا ليس عامًا؛أو الأفضل من ذلك، رفض أي نوع آخر عن طريق التحقق من نوعه.

ما هو الهدف من التمرين؟

كما أشار الأشخاص بالفعل، يمكن أن يكون لديك وظيفة غير عامة تأخذ أكبر عنصر، وسيقوم المترجم تلقائيًا بتحويل ints أصغر لك.

static bool IntegerFunction(Int64 value) { }

إذا كانت وظيفتك على المسار الحرج للأداء (وهو أمر مستبعد جدًا، IMO)، فيمكنك توفير التحميل الزائد لجميع الوظائف المطلوبة.

static bool IntegerFunction(Int64 value) { }
...
static bool IntegerFunction(Int16 value) { }

سأستخدم واحدًا عامًا يمكنك التعامل معه خارجيًا ...

/// <summary>
/// Generic object copy of the same type
/// </summary>
/// <typeparam name="T">The type of object to copy</typeparam>
/// <param name="ObjectSource">The source object to copy</param>
public T CopyObject<T>(T ObjectSource)
{
    T NewObject = System.Activator.CreateInstance<T>();

    foreach (PropertyInfo p in ObjectSource.GetType().GetProperties())
        NewObject.GetType().GetProperty(p.Name).SetValue(NewObject, p.GetValue(ObjectSource, null), null);

    return NewObject;
}

لقد أثر هذا القيد علي عندما حاولت زيادة التحميل على عوامل التشغيل للأنواع العامة؛نظرًا لعدم وجود قيد "INumeric"، ولمجموعة من الأسباب الأخرى التي يسعد الأشخاص الجيدون في Stackoverflow بتقديمها، لا يمكن تعريف العمليات على أنواع عامة.

أردت شيئا من هذا القبيل

public struct Foo<T>
{
    public T Value{ get; private set; }

    public static Foo<T> operator +(Foo<T> LHS, Foo<T> RHS)
    {
        return new Foo<T> { Value = LHS.Value + RHS.Value; };
    }
}

لقد تعاملت مع هذه المشكلة باستخدام كتابة وقت التشغيل الديناميكي .net4.

public struct Foo<T>
{
    public T Value { get; private set; }

    public static Foo<T> operator +(Foo<T> LHS, Foo<T> RHS)
    {
        return new Foo<T> { Value = LHS.Value + (dynamic)RHS.Value };
    }
}

الأمران المتعلقان بالاستخدام dynamic نكون

  1. أداء.يتم وضع جميع أنواع القيم في صندوق.
  2. أخطاء وقت التشغيل.أنت "تغلب" على المترجم، لكنك تفقد أمان الكتابة.إذا لم يتم تحديد عامل التشغيل للنوع العام، فسيتم طرح استثناء أثناء التنفيذ.

لا يوجد حل "جيد" لهذا حتى الآن.ومع ذلك، يمكنك تضييق وسيطة النوع بشكل ملحوظ لاستبعاد العديد من الأخطاء في القيد الافتراضي "INumeric" الخاص بك كما أوضح Haacked أعلاه.

المنطق المنطقي الثابت IntegerFunction<T>(قيمة T) حيث T:icomparable ، غير قابلة للشفاء ، iconvertible ، icomparableu003CT> ، iequatableu003CT> ، بنية {...

لا تتشارك الأنواع الرقمية الأولية .NET في أي واجهة مشتركة تسمح باستخدامها في العمليات الحسابية.سيكون من الممكن تحديد واجهاتك الخاصة (على سبيل المثال. ISignedWholeNumber) التي من شأنها تنفيذ مثل هذه العمليات، وتحديد الهياكل التي تحتوي على واحد Int16, Int32, ، إلخ.وتنفيذ تلك الواجهات، ومن ثم الحصول على طرق تقبل الأنواع العامة المقيدة بها ISignedWholeNumber, ، ولكن من المحتمل أن يكون الاضطرار إلى تحويل القيم الرقمية إلى أنواع البنية الخاصة بك أمرًا مزعجًا.

سيكون النهج البديل هو تحديد فئة ثابتة Int64Converter<T> مع خاصية ثابتة bool Available {get;}; والمندوبين ثابتة ل Int64 GetInt64(T value), T FromInt64(Int64 value), bool TryStoreInt64(Int64 value, ref T dest).يمكن استخدام مُنشئ الفئة بشكل ثابت لتحميل المفوضين للأنواع المعروفة، وربما استخدام الانعكاس لاختبار ما إذا كان النوع T ينفذ أساليب بالأسماء والتوقيعات الصحيحة (في حالة ما يشبه البنية التي تحتوي على ملف Int64 ويمثل رقما، ولكن له عرف ToString() طريقة).سيفقد هذا الأسلوب المزايا المرتبطة بفحص النوع في وقت الترجمة، لكنه سيظل قادرًا على تجنب عمليات الملاكمة وسيتعين "فحص" كل نوع مرة واحدة فقط.وبعد ذلك، سيتم استبدال العمليات المرتبطة بهذا النوع بإرسال مندوب.

إذا كنت تستخدم .NET 4.0 والإصدارات الأحدث، فيمكنك استخدامه فقط متحرك كوسيطة الأسلوب والتحقق في وقت التشغيل أن مرت متحرك نوع الوسيطة هو نوع رقمي/عدد صحيح.

إذا كان نوع من مرت متحرك يكون لا نوع رقمي/عدد صحيح ثم قم بطرح الاستثناء.

مثال قصير الكود الذي ينفذ الفكرة هو شيء مثل:

using System;
public class InvalidArgumentException : Exception
{
    public InvalidArgumentException(string message) : base(message) {}
}
public class InvalidArgumentTypeException : InvalidArgumentException
{
    public InvalidArgumentTypeException(string message) : base(message) {}
}
public class ArgumentTypeNotIntegerException : InvalidArgumentTypeException
{
    public ArgumentTypeNotIntegerException(string message) : base(message) {}
}
public static class Program
{
    private static bool IntegerFunction(dynamic n)
    {
        if (n.GetType() != typeof(Int16) &&
            n.GetType() != typeof(Int32) &&
            n.GetType() != typeof(Int64) &&
            n.GetType() != typeof(UInt16) &&
            n.GetType() != typeof(UInt32) &&
            n.GetType() != typeof(UInt64))
            throw new ArgumentTypeNotIntegerException("argument type is not integer type");
        //code that implements IntegerFunction goes here
    }
    private static void Main()
    {
         Console.WriteLine("{0}",IntegerFunction(0)); //Compiles, no run time error and first line of output buffer is either "True" or "False" depends on the code that implements "Program.IntegerFunction" static method.
         Console.WriteLine("{0}",IntegerFunction("string")); //Also compiles but it is run time error and exception of type "ArgumentTypeNotIntegerException" is thrown here.
         Console.WriteLine("This is the last Console.WriteLine output"); //Never reached and executed due the run time error and the exception thrown on the second line of Program.Main static method.
    }

بالطبع أن هذا الحل يعمل في وقت التشغيل فقط ولكن لا يعمل أبدًا في وقت الترجمة.

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

فمن المنطقي أن ملفوفة متحرك دائما خاص عضو في الفئة/البنية وهو العضو الوحيد في البنية/الفئة واسم العضو الوحيد في البنية/الفئة هو "القيمة".

سيكون عليك أيضًا التحديد والتنفيذ عام الأساليب و/أو العوامل التي تعمل مع الأنواع المطلوبة للعضو الديناميكي الخاص للفئة/البنية إذا لزم الأمر.

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

بمجرد أن تصبح الفئة/البنية جاهزة، حدد نوع الوسيطة IntegerFunction لتكون تلك الفئة/البنية التي تم تعريفها.

مثال طويل الكود الذي ينفذ الفكرة هو شيء مثل:

using System;
public struct Integer
{
    private dynamic value;
    private Integer(dynamic n) { this.value = n; }
    public Integer(Int16 n) { this.value = n; }
    public Integer(Int32 n) { this.value = n; }
    public Integer(Int64 n) { this.value = n; }
    public Integer(UInt16 n) { this.value = n; }
    public Integer(UInt32 n) { this.value = n; }
    public Integer(UInt64 n) { this.value = n; }
    public Integer(Integer n) { this.value = n.value; }
    public static implicit operator Int16(Integer n) { return n.value; }
    public static implicit operator Int32(Integer n) { return n.value; }
    public static implicit operator Int64(Integer n) { return n.value; }
    public static implicit operator UInt16(Integer n) { return n.value; }
    public static implicit operator UInt32(Integer n) { return n.value; }
    public static implicit operator UInt64(Integer n) { return n.value; }
    public static Integer operator +(Integer x, Int16 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, Int32 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, Int64 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, UInt16 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, UInt32 y) { return new Integer(x.value + y); }
    public static Integer operator +(Integer x, UInt64 y) { return new Integer(x.value + y); }
    public static Integer operator -(Integer x, Int16 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, Int32 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, Int64 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, UInt16 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, UInt32 y) { return new Integer(x.value - y); }
    public static Integer operator -(Integer x, UInt64 y) { return new Integer(x.value - y); }
    public static Integer operator *(Integer x, Int16 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, Int32 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, Int64 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, UInt16 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, UInt32 y) { return new Integer(x.value * y); }
    public static Integer operator *(Integer x, UInt64 y) { return new Integer(x.value * y); }
    public static Integer operator /(Integer x, Int16 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, Int32 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, Int64 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, UInt16 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, UInt32 y) { return new Integer(x.value / y); }
    public static Integer operator /(Integer x, UInt64 y) { return new Integer(x.value / y); }
    public static Integer operator %(Integer x, Int16 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, Int32 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, Int64 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, UInt16 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, UInt32 y) { return new Integer(x.value % y); }
    public static Integer operator %(Integer x, UInt64 y) { return new Integer(x.value % y); }
    public static Integer operator +(Integer x, Integer y) { return new Integer(x.value + y.value); }
    public static Integer operator -(Integer x, Integer y) { return new Integer(x.value - y.value); }
    public static Integer operator *(Integer x, Integer y) { return new Integer(x.value * y.value); }
    public static Integer operator /(Integer x, Integer y) { return new Integer(x.value / y.value); }
    public static Integer operator %(Integer x, Integer y) { return new Integer(x.value % y.value); }
    public static bool operator ==(Integer x, Int16 y) { return x.value == y; }
    public static bool operator !=(Integer x, Int16 y) { return x.value != y; }
    public static bool operator ==(Integer x, Int32 y) { return x.value == y; }
    public static bool operator !=(Integer x, Int32 y) { return x.value != y; }
    public static bool operator ==(Integer x, Int64 y) { return x.value == y; }
    public static bool operator !=(Integer x, Int64 y) { return x.value != y; }
    public static bool operator ==(Integer x, UInt16 y) { return x.value == y; }
    public static bool operator !=(Integer x, UInt16 y) { return x.value != y; }
    public static bool operator ==(Integer x, UInt32 y) { return x.value == y; }
    public static bool operator !=(Integer x, UInt32 y) { return x.value != y; }
    public static bool operator ==(Integer x, UInt64 y) { return x.value == y; }
    public static bool operator !=(Integer x, UInt64 y) { return x.value != y; }
    public static bool operator ==(Integer x, Integer y) { return x.value == y.value; }
    public static bool operator !=(Integer x, Integer y) { return x.value != y.value; }
    public override bool Equals(object obj) { return this == (Integer)obj; }
    public override int GetHashCode() { return this.value.GetHashCode(); }
    public override string ToString() { return this.value.ToString(); }
    public static bool operator >(Integer x, Int16 y) { return x.value > y; }
    public static bool operator <(Integer x, Int16 y) { return x.value < y; }
    public static bool operator >(Integer x, Int32 y) { return x.value > y; }
    public static bool operator <(Integer x, Int32 y) { return x.value < y; }
    public static bool operator >(Integer x, Int64 y) { return x.value > y; }
    public static bool operator <(Integer x, Int64 y) { return x.value < y; }
    public static bool operator >(Integer x, UInt16 y) { return x.value > y; }
    public static bool operator <(Integer x, UInt16 y) { return x.value < y; }
    public static bool operator >(Integer x, UInt32 y) { return x.value > y; }
    public static bool operator <(Integer x, UInt32 y) { return x.value < y; }
    public static bool operator >(Integer x, UInt64 y) { return x.value > y; }
    public static bool operator <(Integer x, UInt64 y) { return x.value < y; }
    public static bool operator >(Integer x, Integer y) { return x.value > y.value; }
    public static bool operator <(Integer x, Integer y) { return x.value < y.value; }
    public static bool operator >=(Integer x, Int16 y) { return x.value >= y; }
    public static bool operator <=(Integer x, Int16 y) { return x.value <= y; }
    public static bool operator >=(Integer x, Int32 y) { return x.value >= y; }
    public static bool operator <=(Integer x, Int32 y) { return x.value <= y; }
    public static bool operator >=(Integer x, Int64 y) { return x.value >= y; }
    public static bool operator <=(Integer x, Int64 y) { return x.value <= y; }
    public static bool operator >=(Integer x, UInt16 y) { return x.value >= y; }
    public static bool operator <=(Integer x, UInt16 y) { return x.value <= y; }
    public static bool operator >=(Integer x, UInt32 y) { return x.value >= y; }
    public static bool operator <=(Integer x, UInt32 y) { return x.value <= y; }
    public static bool operator >=(Integer x, UInt64 y) { return x.value >= y; }
    public static bool operator <=(Integer x, UInt64 y) { return x.value <= y; }
    public static bool operator >=(Integer x, Integer y) { return x.value >= y.value; }
    public static bool operator <=(Integer x, Integer y) { return x.value <= y.value; }
    public static Integer operator +(Int16 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(Int32 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(Int64 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(UInt16 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(UInt32 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator +(UInt64 x, Integer y) { return new Integer(x + y.value); }
    public static Integer operator -(Int16 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(Int32 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(Int64 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(UInt16 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(UInt32 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator -(UInt64 x, Integer y) { return new Integer(x - y.value); }
    public static Integer operator *(Int16 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(Int32 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(Int64 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(UInt16 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(UInt32 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator *(UInt64 x, Integer y) { return new Integer(x * y.value); }
    public static Integer operator /(Int16 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(Int32 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(Int64 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(UInt16 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(UInt32 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator /(UInt64 x, Integer y) { return new Integer(x / y.value); }
    public static Integer operator %(Int16 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(Int32 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(Int64 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(UInt16 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(UInt32 x, Integer y) { return new Integer(x % y.value); }
    public static Integer operator %(UInt64 x, Integer y) { return new Integer(x % y.value); }
    public static bool operator ==(Int16 x, Integer y) { return x == y.value; }
    public static bool operator !=(Int16 x, Integer y) { return x != y.value; }
    public static bool operator ==(Int32 x, Integer y) { return x == y.value; }
    public static bool operator !=(Int32 x, Integer y) { return x != y.value; }
    public static bool operator ==(Int64 x, Integer y) { return x == y.value; }
    public static bool operator !=(Int64 x, Integer y) { return x != y.value; }
    public static bool operator ==(UInt16 x, Integer y) { return x == y.value; }
    public static bool operator !=(UInt16 x, Integer y) { return x != y.value; }
    public static bool operator ==(UInt32 x, Integer y) { return x == y.value; }
    public static bool operator !=(UInt32 x, Integer y) { return x != y.value; }
    public static bool operator ==(UInt64 x, Integer y) { return x == y.value; }
    public static bool operator !=(UInt64 x, Integer y) { return x != y.value; }
    public static bool operator >(Int16 x, Integer y) { return x > y.value; }
    public static bool operator <(Int16 x, Integer y) { return x < y.value; }
    public static bool operator >(Int32 x, Integer y) { return x > y.value; }
    public static bool operator <(Int32 x, Integer y) { return x < y.value; }
    public static bool operator >(Int64 x, Integer y) { return x > y.value; }
    public static bool operator <(Int64 x, Integer y) { return x < y.value; }
    public static bool operator >(UInt16 x, Integer y) { return x > y.value; }
    public static bool operator <(UInt16 x, Integer y) { return x < y.value; }
    public static bool operator >(UInt32 x, Integer y) { return x > y.value; }
    public static bool operator <(UInt32 x, Integer y) { return x < y.value; }
    public static bool operator >(UInt64 x, Integer y) { return x > y.value; }
    public static bool operator <(UInt64 x, Integer y) { return x < y.value; }
    public static bool operator >=(Int16 x, Integer y) { return x >= y.value; }
    public static bool operator <=(Int16 x, Integer y) { return x <= y.value; }
    public static bool operator >=(Int32 x, Integer y) { return x >= y.value; }
    public static bool operator <=(Int32 x, Integer y) { return x <= y.value; }
    public static bool operator >=(Int64 x, Integer y) { return x >= y.value; }
    public static bool operator <=(Int64 x, Integer y) { return x <= y.value; }
    public static bool operator >=(UInt16 x, Integer y) { return x >= y.value; }
    public static bool operator <=(UInt16 x, Integer y) { return x <= y.value; }
    public static bool operator >=(UInt32 x, Integer y) { return x >= y.value; }
    public static bool operator <=(UInt32 x, Integer y) { return x <= y.value; }
    public static bool operator >=(UInt64 x, Integer y) { return x >= y.value; }
    public static bool operator <=(UInt64 x, Integer y) { return x <= y.value; }
}
public static class Program
{
    private static bool IntegerFunction(Integer n)
    {
        //code that implements IntegerFunction goes here
        //note that there is NO code that checks the type of n in rum time, because it is NOT needed anymore 
    }
    private static void Main()
    {
        Console.WriteLine("{0}",IntegerFunction(0)); //compile error: there is no overloaded METHOD for objects of type "int" and no implicit conversion from any object, including "int", to "Integer" is known.
        Console.WriteLine("{0}",IntegerFunction(new Integer(0))); //both compiles and no run time error
        Console.WriteLine("{0}",IntegerFunction("string")); //compile error: there is no overloaded METHOD for objects of type "string" and no implicit conversion from any object, including "string", to "Integer" is known.
        Console.WriteLine("{0}",IntegerFunction(new Integer("string"))); //compile error: there is no overloaded CONSTRUCTOR for objects of type "string"
    }
}

لاحظ أنه من أجل الاستخدام متحرك في التعليمات البرمجية الخاصة بك يجب عليك يضيف مرجعا ل مايكروسوفت سي شارب

إذا كان إصدار .NET Framework أقل/أقل/أقل من 4.0 و متحرك غير محدد في هذا الإصدار، فسيتعين عليك استخدامه هدف بدلاً من ذلك، قم بالإرسال إلى النوع الصحيح، وهو أمر يمثل مشكلة، لذا أوصي باستخدام .NET 4.0 على الأقل أو إصدار أحدث إذا استطعت حتى تتمكن من استخدامه متحرك بدلاً من هدف.

إذا كان كل ما تريده هو الاستخدام نوع رقمي واحد, ، يمكنك التفكير في إنشاء شيء مشابه لاسم مستعار في C++ باستخدام using.

لذلك بدلاً من الحصول على عام جدًا

T ComputeSomething<T>(T value1, T value2) where T : INumeric { ... }

يمكن أن يكون لديك

using MyNumType = System.Double;
T ComputeSomething<MyNumType>(MyNumType value1, MyNumType value2) { ... }

قد يسمح لك ذلك بالانتقال بسهولة من double ل int أو غيرها إذا لزم الأمر، ولكنك لن تكون قادرا على استخدامها ComputeSomething مع double و int في نفس البرنامج.

ولكن لماذا لا يتم استبدال الكل double ل int ثم؟لأن طريقتك قد ترغب في استخدام ملف double ما إذا كان الإدخال double أو int.يتيح لك الاسم المستعار معرفة المتغير الذي يستخدم متحرك يكتب.

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

مرة أخرى، مثل العديد من الأشخاص، نظرت إلى القيود وتوصلت إلى مجموعة من الواجهات التي كان عليها أن تدعمها.ومع ذلك، أ) لم تكن مانعة لتسرب الماء بنسبة 100%، وب) أي شخص جديد ينظر إلى هذه القائمة الطويلة من القيود سوف يشعر بالارتباك الشديد على الفور.

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

public static string DoSomething(this int input, ...) => DoSomethingHelper(input, ...);
public static string DoSomething(this decimal input, ...) => DoSomethingHelper(input, ...);
public static string DoSomething(this double input, ...) => DoSomethingHelper(input, ...);
public static string DoSomething(this string input, ...) => DoSomethingHelper(input, ...);

private static string DoSomethingHelper<T>(this T input, ....)
{
    // complex logic
}

لا توجد واجهة واحدة أو فئة أساسية يرثونها جميعًا (والتي لا ترثها الفئات الأخرى أيضًا) لذا فإن الإجابة البسيطة هي لا.

أنا لا أتساءل لماذا هذه مشكلة بالرغم من ذلك.ما الذي تريد القيام به داخل فئة IntegerFunction الخاصة بك والذي لا يمكن القيام به إلا للأعداد الصحيحة؟

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

أيضًا، نظرًا لأنك تريد فقط السماح للوظيفة بالعمل على أنواع البيانات int، فلن تحتاج إلى وظيفة منفصلة لكل حجم محدد.إن مجرد أخذ معلمة في أكبر نوع محدد سيسمح للبرنامج بتحديث أنواع البيانات الأصغر إليه تلقائيًا.(أي.سيؤدي تمرير Int16 إلى التحويل تلقائيًا إلى Int64 عند الاتصال).

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

إذا فشل كل شيء آخر، يمكن استخدام معلمة من النوع Object وبعد ذلك سيتعين عليك التحقق من نوع المعلمة واتخاذ الإجراء المناسب أو طرح استثناء.

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