كيف يمكنني الجمع بين العديد من التعبيرات في طريقة سريعة؟

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

  •  19-09-2019
  •  | 
  •  

سؤال

افترض أن لدي التعبيرات التالية:

Expression<Action<T, StringBuilder>> expr1 = (t, sb) => sb.Append(t.Name);
Expression<Action<T, StringBuilder>> expr2 = (t, sb) => sb.Append(", ");
Expression<Action<T, StringBuilder>> expr3 = (t, sb) => sb.Append(t.Description);

أود أن أكون قادرا على تجميعها في طريقة / مندوبا ما يعادل ما يلي:

void Method(T t, StringBuilder sb) 
{
    sb.Append(t.Name);
    sb.Append(", ");
    sb.Append(t.Description);
}

ما هي أفضل طريقة لمعالجة هذا؟ أود أن أداء جيدا، من الناحية المثالية مع أداء ما يعادل الطريقة المذكورة أعلاه.

تحديثلذلك، في حين يبدو أنه لا توجد وسيلة للقيام بذلك مباشرة في C # 3 هل هناك طريقة لتحويل تعبير إلى Il حتى أتمكن من استخدامها مع system.rection.emit؟

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

المحلول

لسوء الحظ في .NET 3.5 لا يمكنك بناء تعبير يؤدي سلسلة من العمليات التعسفية. إليك قائمة التعبيرات المدعومة:

  • حسابي: إضافة، مدفوعة المدفوعة، تقسيم، موكب، مضاعفة، مضاعفة، نفي، negatechecked، power، طرح، متضللها، unaryplus
  • إنشاء: BIND، Elementinit، ListBind، Listinit، MemberBind، MountainInit، New، NewarrayBounds، Newarrayinit
  • bitwise: و، acriscor، receshift (<<)، لا، أو، raftshift (>>)
  • المنطقي: Andalso (&&)، الحالة (؟ :)، يساوي، أشرطة أكبر، أشرطة أكبر، أقل، أقل، نطق، orelse (||)،
  • وصول الأعضاء: ArrayIndex، الطول الرئيسي، مكالمة، الحقل، الممتلكات، Propertyorfield
  • اخر: تحويل، convertchecked، coalesce (؟؟)، ثابت، استدعاء، لامبادا، المعلمة، typeas، اقتباس

.NET 4 يمتد هذا API عن طريق إضافة التعبيرات التالية:

  • طفرة: addassign، adsassignchecked، andassign، التسمية، التفضيل، الحصري، moduloassign، multiplyassign، orassign، postdecrementassign، postincrementassign، powerassign، resecrementassign، preincrementassign، shiftassign، subactractissignchecked
  • حسابي: انخفاض، الافتراضي، الزيادة، onscomplement
  • وصول الأعضاء: Arrayaccess، الديناميكي
  • المنطقي: التعايد، إحالة SeneCenoteQual، Typequal
  • التدفق: كتلة، استراحة، متابعة، فارغة، goto، ifthen، ifthenelse، iffalse، iftrue، التسمية، حلقة، العودة، التبديل، switchcase، unbox، متغير
  • الاستثناءات: الصيد، ريثرو، رمي
  • تصحيح: Cleardebuginfo، Debuginfo

ال منع التعبير مثير للاهتمام بشكل خاص.

نصائح أخرى

يمكنك، لكنها ليست مهمة تافهة.

عندما يكون لديك متغير من تعبير النوع، يمكنك فحص خاصية الجسم للعثور على بنية البيانات للتعبير.

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

مثلما يجمع LinQ-To-SQL التعبير إلى استعلام SQL، فهل يمكنك ترجمة تعبيراتك إلى كل ما تحتاجه. سيكون لديك الكثير من العمل أمامك، لكنك تحتاج فقط إلى تنفيذ هذا ما تريد دعمه.

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

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

ومع ذلك، يمكنك القيام بذلك عن طريق استغلال حقيقة ذلك StringBuilder يكشف "بطلاقة" واجهة برمجة تطبيقات "بطلاقة"؛ لذلك بدلا من Action<T,StringBuilder> لديك أ Func<T,StringBuilder,StringBuilder> - مثل أدناه (لاحظ أن بناء الجملة الفعلي للتعبير عن هذه التعبيرات هو مطابق في هذه الحالة):

class Program
{
    static void Main()
    {
        Foo(new MyType { Name = "abc", Description = "def" });
    }
    static void Foo<T>(T val) where T : IMyType
    {
        var expressions = new Expression<Func<T, StringBuilder, StringBuilder>>[] {
                (t, sb) => sb.Append(t.Name),
                (t, sb) => sb.Append(", "),
                (t, sb) => sb.Append(t.Description)
        };
        var tparam = Expression.Parameter(typeof(T), "t");
        var sbparam = Expression.Parameter(typeof(StringBuilder), "sb");

        Expression body = sbparam;
        for (int i = 0; i < expressions.Length; i++)
        {
            body = Expression.Invoke(expressions[i], tparam, body);
        }
        var func = Expression.Lambda<Func<T, StringBuilder, StringBuilder>>(
            body, tparam, sbparam).Compile();

        // now test it
        StringBuilder sbInst = new StringBuilder();
        func(val, sbInst);
        Console.WriteLine(sbInst.ToString());
    }
}
public class MyType : IMyType
{
    public string Name { get; set; }
    public string Description { get; set; }
}
interface IMyType
{
    string Name { get; }
    string Description { get; }
}

فمن المؤكد أنها ل ممكن لتفقد الأشجار ونبعث IL يدويا (DynamicMethod ربما)، ولكن عليك اتخاذ بعض القرارات حول الحد من التعقيد. للحصول على الكود كما قدمت أنا يمكن أن تفعل ذلك في مسؤول الوقت (لا يزال غير تافه)، ولكن إذا كنت تتوقع أي شيء أكثر تعقيدا Expression هو أكثر مقلي.

يمكنك فقط القيام بذلك في .NET 4. آسف لا أعرف التفاصيل.

تعديل:

إذا كنت مرتاحا مع الانعكاس. هل يمكن أن تنبعث منه طريقة استدعاء هذه التعبيرات بالتسلسل.

بديل آخر:

إنشاء طريقة "Do"، أي:

void Do(params Action[] actions)
{
  foreach (var a in actions) a();
}

هناك طريقة أخرى للنظر في هذه المشكلة هي أن تتذكر أن المندوبين متعددون؛ يمكنك الجمع بين Action مرات عديدة؛

class Program
{
    static void Main()
    {
        Foo(new MyType { Name = "abc", Description = "def" });
    }

    static void Foo<T>(T val) where T : IMyType {
        var expressions = new Expression<Action<T, StringBuilder>>[] {
                (t, sb) => sb.Append(t.Name),
                (t, sb) => sb.Append(", "),
                (t, sb) => sb.Append(t.Description)
        };
        Action<T, StringBuilder> result = null;
        foreach (var expr in expressions) result += expr.Compile();
        if (result == null) result = delegate { };
        // now test it
        StringBuilder sbInst = new StringBuilder();
        result(val, sbInst);
        Console.WriteLine(sbInst.ToString());
    }
}
public class MyType : IMyType
{
    public string Name { get; set; }
    public string Description { get; set; }
}
interface IMyType
{
    string Name { get; }
    string Description { get; }

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