। নেট ফানক <T> কে। নেট এক্সপ্রেশন <ফানক <টি>> এ রূপান্তর করা হচ্ছে


118

ল্যাম্বডা থেকে এক্সপ্রেশনে যাওয়া কোনও পদ্ধতি কল ব্যবহার করা সহজ ...

public void GimmeExpression(Expression<Func<T>> expression)
{
    ((MemberExpression)expression.Body).Member.Name; // "DoStuff"
}

public void SomewhereElse()
{
    GimmeExpression(() => thing.DoStuff());
}

তবে আমি ফানককে একটি অভিব্যক্তিতে পরিণত করতে চাই, কেবল বিরল ক্ষেত্রেই ...

public void ContainTheDanger(Func<T> dangerousCall)
{
    try 
    {
        dangerousCall();
    }
    catch (Exception e)
    {
        // This next line does not work...
        Expression<Func<T>> DangerousExpression = dangerousCall;
        var nameOfDanger = 
            ((MemberExpression)dangerousCall.Body).Member.Name;
        throw new DangerContainer(
            "Danger manifested while " + nameOfDanger, e);
    }
}

public void SomewhereElse()
{
    ContainTheDanger(() => thing.CrossTheStreams());
}

যে লাইনটি কাজ করে না তা আমাকে সংকলন-সময় ত্রুটি দেয় Cannot implicitly convert type 'System.Func<T>' to 'System.Linq.Expressions.Expression<System.Func<T>>'। একটি সুস্পষ্ট কাস্ট পরিস্থিতি সমাধান করে না। এটি করার কোনও সুবিধা কি আমি অবহেলিত করছি?


আমি 'বিরল ক্ষেত্রে' উদাহরণের জন্য খুব বেশি ব্যবহার দেখতে পাচ্ছি না। কলকারী ফানক <T> এ যাচ্ছেন। সেই ফানক <টি> কী ছিল (ব্যতিক্রমের মধ্য দিয়ে) তা কলকারীর কাছে ফিরে আসার দরকার নেই।
অ্যাডাম রাল্ফ

2
ব্যতিক্রম কলারে পরিচালিত হয় না। এবং, কারণ বিভিন্ন ফানক <T> গুলিগুলিতে একাধিক কল সাইটগুলি অতিক্রম করছে, কলারে ব্যতিক্রম ধরা নকল তৈরি করে।
ডেভ ক্যামেরন

1
ব্যতিক্রম স্ট্যাক ট্রেস এই তথ্যটি দেখানোর জন্য ডিজাইন করা হয়েছে। যদি ব্যতিক্রমটি ফানক <টি> এর অনুরোধের মধ্যে ফেলে দেওয়া হয় তবে এটি স্ট্যাক ট্রেসে প্রদর্শিত হবে। ঘটনাক্রমে, আপনি যদি অন্যভাবে যেতে পছন্দ করেন, অর্থাত্ কোনও অভিব্যক্তি গ্রহণ করেন এবং আহবানের জন্য এটি সংকলন করেন তবে আপনি এটি হারাবেন যেহেতু স্ট্যাক ট্রেস at lambda_method(Closure )সংকলিত প্রতিনিধিদের অনুরোধের মতো কিছু দেখায় ।
অ্যাডাম রাল্ফ

আমার ধারণা এই উত্তরটি আপনার [লিঙ্ক] [১] [১]: স্ট্যাকওভারফ্লো
ইব্রাহিম কাইস ইব্রাহিম ২

উত্তর:


104

ওহ, এটি মোটেও সহজ নয়। Func<T>একটি জেনেরিক উপস্থাপন করে একটি delegateঅভিব্যক্তি নয়। যদি আপনি এটি করতে পারেন এমন কোনও উপায় থাকে (সংযোজনকারীর দ্বারা অপ্টিমাইজেশন এবং অন্যান্য কাজগুলির কারণে কিছু ডেটা ফেলে দেওয়া হতে পারে, তবে আসল অভিব্যক্তিটি ফিরে পাওয়া অসম্ভব হতে পারে), এটি ফ্লাইতে আইএল বিচ্ছিন্ন করে দিবে ' এবং অভিব্যক্তি অনুমান করা (যা কোনওভাবেই সহজ নয়)। ল্যাম্বডা এক্সপ্রেশনগুলি ডেটা হিসাবে ব্যবহার করা ( Expression<Func<T>>) মূলত সংকলক দ্বারা করা একটি যাদু (মূলত সংকলক আইএলটিতে সংকলনের পরিবর্তে কোডে একটি এক্সপ্রেশন ট্রি তৈরি করে)।

সম্পর্কিত ঘটনা

এ কারণেই ল্যাম্বডাসকে চূড়ান্ত দিকে ঠেলে দেওয়ার মতো ভাষা (লিস্পের মতো) প্রায়শই দোভাষী হিসাবে প্রয়োগ করা সহজ হয় । এই ভাষাগুলিতে কোড এবং ডেটা মূলত একই জিনিস (এমনকি রান সময়েও ) তবে আমাদের চিপ কোডের সেই রূপটি বুঝতে পারে না, সুতরাং এর উপরে একটি দোভাষী তৈরি করে আমাদের এমন একটি মেশিন অনুকরণ করতে হবে যা এটি বোঝে ( ভাষাগুলির মতো লিস্প দ্বারা তৈরি করা পছন্দ) বা কিছু পরিমাণে (কোড আর তথ্যের সাথে একেবারে সমান হবে না) ক্ষমতা বলিদান (সি # দ্বারা করা পছন্দ)। সি # তে, সংকলক ল্যাম্বডাসকে সংকলনের সময় কোড ( Func<T>) এবং ডেটা ( Expression<Func<T>>) হিসাবে ব্যাখ্যা করার অনুমতি দিয়ে ডেটা হিসাবে কোড চিকিত্সার বিভ্রম দেয় ।


3
লিস্পকে ব্যাখ্যা করার দরকার নেই, এটি সহজেই সংকলন করা যায়। সংকলনের সময় ম্যাক্রোগুলি প্রসারিত করতে হবে এবং আপনি সমর্থন evalকরতে চাইলে আপনাকে সংকলকটি শুরু করতে হবে, তবে অন্যটি ছাড়া, এটি করার ক্ষেত্রে কোনও সমস্যা নেই।
Configurator

2
"এক্সপ্রেশন <ফানক <টি>> বিপজ্জনক এক্সপ্রেশন = () => বিপজ্জনক কল ();" সহজ না?
মহিমান

10
@ মাহেমান এটি Expressionআপনার মোড়ক অ্যাকশন সম্পর্কে নতুন তৈরি করবে , তবে dangerousCallপ্রতিনিধিদের অভ্যন্তরীণ সম্পর্কে এটির কোনও অভিব্যক্তি গাছের তথ্য নেই ।
নিনাদ

34
    private static Expression<Func<T, bool>> FuncToExpression<T>(Func<T, bool> f)  
    {  
        return x => f(x);  
    } 

1
আমি ফিরে আসা এক্সপ্রেশনটির সিনট্যাক্স ট্রিটি অতিক্রম করতে চেয়েছিলাম। এই দৃষ্টিভঙ্গি কি আমাকে এটি করার অনুমতি দেবে?
ডেভ ক্যামেরন

6
@ ডেভ ক্যামেরন - নং উপরের উত্তরগুলি দেখুন - ইতিমধ্যে সংকলিত Funcএকটি নতুন এক্সপ্রেশনে লুকানো থাকবে। এটি কেবল কোডের সাথে ডেটার একটি স্তর যুক্ত করে; আপনি fআরও বিশদ ছাড়াই আপনার প্যারামিটারটি সন্ধান করতে একটি স্তরকে অতিক্রম করতে পারেন, তাই আপনি যেখানেই শুরু করেছিলেন ঠিক সেদিকেই।
জনো

21

আপনার সম্ভবত যা করা উচিত তা হ'ল পদ্ধতিটি ঘুরিয়ে দেওয়া। একটি এক্সপ্রেশন> নিন এবং সংকলন করুন এবং চালান। যদি এটি ব্যর্থ হয়, আপনার কাছে ইতিমধ্যে সন্ধানের জন্য এক্সপ্রেশন রয়েছে।

public void ContainTheDanger(Expression<Func<T>> dangerousCall)
{
    try 
    {
        dangerousCall().Compile().Invoke();;
    }
    catch (Exception e)
    {
        // This next line does not work...
        var nameOfDanger = 
            ((MemberExpression)dangerousCall.Body).Member.Name;
        throw new DangerContainer(
            "Danger manifested while " + nameOfDanger, e);
    }
}

public void SomewhereElse()
{
    ContainTheDanger(() => thing.CrossTheStreams());
}

স্পষ্টতই আপনাকে এর পারফরম্যান্সের প্রভাবগুলি বিবেচনা করতে হবে এবং এটি নির্ধারণ করা উচিত যে এটি আসলে আপনার কিছু করা দরকার।


7

আপনি কমপাইল () পদ্ধতির মাধ্যমে অন্য পথে যেতে পারেন - তবে এটি নিশ্চিত নয় যে এটি আপনার পক্ষে কার্যকর কিনা:

public void ContainTheDanger<T>(Expression<Func<T>> dangerousCall)
{
    try
    {
        var expr = dangerousCall.Compile();
        expr.Invoke();
    }
    catch (Exception e)
    {
        Expression<Func<T>> DangerousExpression = dangerousCall;
        var nameOfDanger = ((MethodCallExpression)dangerousCall.Body).Method.Name;
        throw new DangerContainer("Danger manifested while " + nameOfDanger, e);
    }
}

public void SomewhereElse()
{
    var thing = new Thing();
    ContainTheDanger(() => thing.CrossTheStreams());
}

6

আপনার যদি কখনও কখনও মত প্রকাশের প্রয়োজন হয় এবং কখনও কখনও কোনও প্রতিনিধি প্রয়োজন হয়, আপনার কাছে দুটি বিকল্প রয়েছে:

  • বিভিন্ন পদ্ধতি আছে (প্রতিটি জন্য 1)
  • সর্বদা Expression<...>সংস্করণটি গ্রহণ করুন , এবং .Compile().Invoke(...)যদি আপনি কোনও প্রতিনিধি চান তবে ঠিক এটি। স্পষ্টতই এর ব্যয় হয়েছে।


4
 Expression<Func<T>> ToExpression<T>(Func<T> call)
        {
            MethodCallExpression methodCall = call.Target == null
                ? Expression.Call(call.Method)
                : Expression.Call(Expression.Constant(call.Target), call.Method);

            return Expression.Lambda<Func<T>>(methodCall);
        }

আপনি কি "এটি কাজ করবে না" অংশটি বিশদভাবে বলতে পারেন? আপনি কি আসলে এটিকে সংকলন ও সম্পাদন করার চেষ্টা করেছেন? অথবা এটি বিশেষভাবে আপনার প্রয়োগে কাজ করে না?
দিমিত্রি ডাইজিন

1
এফডব্লিউআইডাব্লু, মূল টিকিটটি এটির মতো নাও হতে পারে তবে এটি আমার প্রয়োজন ছিল। এই call.Targetঅংশটিই আমাকে হত্যা করছিল। এটি কয়েক বছর ধরে কাজ করে, এবং তারপরে হঠাৎ কাজ বন্ধ করে দেয় এবং স্ট্যাটিক / অ-স্থিতিশীল ব্লাহ ব্লাহ সম্পর্কে অভিযোগ শুরু করে। যাইহোক ধন্যবাদ!
এলি গ্যাসসেট


-1

পরিবর্তন

// This next line does not work...
Expression<Func<T>> DangerousExpression = dangerousCall;

প্রতি

// This next line works!
Expression<Func<T>> DangerousExpression = () => dangerousCall();

পরিবেশন করুন, এটি প্রকাশের একদম আইনী উপায়। যদিও এটি তৈরি করতে সিনট্যাক্স চিনিটি এক্সপ্রেশন.লম্বদা এবং এক্সপ্রেশন.ল। রানটাইম কেন এটি ব্যর্থ হওয়া উচিত বলে আপনি মনে করেন?
রোমান পোক্রভস্কিজ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.