'টাইপ স্যুইচ' এর চেয়ে ভাল বিকল্প আছে কি?


331

সি # switchহিসাবে কোনও প্রকারে দেখতে পাওয়া যায় না (যা আমি জড়ো করি তা একটি বিশেষ কেস হিসাবে যুক্ত করা হয়নি কারণ isসম্পর্কের অর্থ একের অধিক স্বতন্ত্র caseপ্রয়োগ হতে পারে), এ ছাড়া অন্য ধরণের স্যুইচিং সিমুলেট করার জন্য আরও ভাল উপায় কি সম্ভব?

void Foo(object o)
{
    if (o is A)
    {
        ((A)o).Hop();
    }
    else if (o is B)
    {
        ((B)o).Skip();
    }
    else
    {
        throw new ArgumentException("Unexpected type: " + o.GetType());
    }
}

18
কৌতূহলের বাইরে, আপনি কেবল পলিমারফিজম ব্যবহার করবেন না কেন?

18
@ জাইউং ক্লাস সিল করেছে এবং এড-হক পরিস্থিতিগুলির জন্য এটি উপযুক্ত নয়
xyz



2
@ জাইউং: একটি সাধারণ পরিস্থিতি যেখানে পলিমারফিজম ব্যবহার করা যায় না তা হ'ল যখন যে ধরণেরগুলি স্যুইচ করা হচ্ছে সেগুলি অবশ্যই switchবিবৃতি সম্বলিত কোডটি জানেন না । একটি উদাহরণ: এসেম্বল এ- তে ডেটা অবজেক্টের একটি সেট রয়েছে (যা কোনও পরিবর্তন করতে যাচ্ছে না, নির্দিষ্টকরণের নথিতে বা এ জাতীয় ক্ষেত্রে সংজ্ঞায়িত)। অ্যাসেম্বলিজ বি , সি , আর ডি প্রতিটি রেফারেন্স একটি থেকে বিভিন্ন তথ্য অবজেক্টের জন্য একটি রূপান্তর প্রদান একটি (যেমন একটি ধারাবাহিকতাতে / deserialization কিছু বিশেষ বিন্যাসে)। আপনি হয় বি , সি , এবং ডি তে পুরো শ্রেণি শ্রেণিবিন্যাসকে আয়না করতে হবে এবং কারখানাগুলি ব্যবহার করতে পারেন, বা আপনার কাছে ...
বা ম্যাপার

উত্তর:


276

প্রকারগুলিতে স্যুইচিংয়ে অবশ্যই সি # এর অভাব রয়েছে ( আপডেট: সি # 7 / ভিএস 2017 তে প্রকারগুলিতে স্যুইচিং সমর্থনযোগ্য - নীচে জাচারি ইয়েটসের উত্তর দেখুন )। বৃহতটি যদি / অন্যটি / অন্য বিবৃতি না দিয়ে এটি করার জন্য আপনাকে আলাদা কাঠামোর সাথে কাজ করতে হবে। আমি টাইপসুইচ কাঠামো কীভাবে তৈরি করব সে সম্পর্কে বিশদ বিবরণে একটি ব্লগ পোস্ট লিখেছিলাম।

https://docs.microsoft.com/archive/blogs/jaredpar/switching-on-types

সংক্ষিপ্ত সংস্করণ: টাইপসুইচটি অপ্রয়োজনীয় ingালাই রোধ করতে এবং এমন একটি সিনট্যাক্স দেওয়ার জন্য ডিজাইন করা হয়েছে যা সাধারণ সুইচ / কেস স্টেটমেন্টের অনুরূপ। উদাহরণস্বরূপ, এখানে একটি স্ট্যান্ডার্ড উইন্ডোজ ফর্ম ইভেন্টের ক্রিয়াকলাপে টাইপসুইচ

TypeSwitch.Do(
    sender,
    TypeSwitch.Case<Button>(() => textBox1.Text = "Hit a Button"),
    TypeSwitch.Case<CheckBox>(x => textBox1.Text = "Checkbox is " + x.Checked),
    TypeSwitch.Default(() => textBox1.Text = "Not sure what is hovered over"));

টাইপসুইচ এর কোডটি আসলে বেশ ছোট এবং সহজেই আপনার প্রকল্পে রাখা যেতে পারে।

static class TypeSwitch {
    public class CaseInfo {
        public bool IsDefault { get; set; }
        public Type Target { get; set; }
        public Action<object> Action { get; set; }
    }

    public static void Do(object source, params CaseInfo[] cases) {
        var type = source.GetType();
        foreach (var entry in cases) {
            if (entry.IsDefault || entry.Target.IsAssignableFrom(type)) {
                entry.Action(source);
                break;
            }
        }
    }

    public static CaseInfo Case<T>(Action action) {
        return new CaseInfo() {
            Action = x => action(),
            Target = typeof(T)
        };
    }

    public static CaseInfo Case<T>(Action<T> action) {
        return new CaseInfo() {
            Action = (x) => action((T)x),
            Target = typeof(T)
        };
    }

    public static CaseInfo Default(Action action) {
        return new CaseInfo() {
            Action = x => action(),
            IsDefault = true
        };
    }
}

26
"টাইপ == এন্ট্রি.টারাজেট" অ্যাকাউন্টে সামঞ্জস্যপূর্ণ প্রকারগুলি (যেমন, উপক্লাস) নিতে "এন্ট্রি.টারাজেট.আইএসএসাইনেবলফর্ম (টাইপ)" এও পরিবর্তন করা যেতে পারে।
মার্ক সিডেদে

"এন্ট্রি.টারাজেট.আইএসএইসনেবলফ্রোম (টাইপ)" ব্যবহার করার জন্য কোডটি এমনভাবে পরিবর্তন করা হয়েছে যাতে উপশ্রেণীগুলি সমর্থিত হয়।
ম্যাট হাওয়েলস

3
একটি বিষয় সম্ভবত লক্ষণীয় যে এটি হ'ল (আমি যা বুঝি সে থেকে) অন্যান্য সমস্ত কেস পরীক্ষা করা হয় তা নিশ্চিত করার জন্য এটি সর্বশেষে 'ডিফল্ট' ক্রিয়া নির্দিষ্ট করা প্রয়োজন। আমি বিশ্বাস করি যে এটি স্ট্যান্ডার্ড সুইচে কোনও প্রয়োজন নয় - এমন নয় যে আমি যে কোনও ভাবে নীচে ব্যতীত অন্য কোথাও 'ডিফল্ট' লাগানোর চেষ্টা করতে দেখিনি। এর জন্য বেশ কয়েকটি ব্যর্থ নিরাপদ বিকল্পগুলি ডিফল্টটি শেষ (কিছুটা অপব্যয়ী) নিশ্চিত করার জন্য অ্যারের অর্ডার করতে পারে বা ডিফল্টটিকে কোনও ভেরিয়েবলের পরে প্রক্রিয়া করার জন্য পপ করতে হবে foreach(যা কেবল কোনও ম্যাচ না পাওয়া গেলে ঘটবে)
musefan

প্রেরক শূন্য হলে কী হবে? গেটটাইপ একটি ব্যতিক্রম ছুঁড়ে ফেলবে
জন

দুটি পরামর্শ: ডিফল্ট কল করে বা একটি ব্যতিক্রম ছুঁড়ে নাল উত্সটি হ্যান্ডেল করুন CaseInfoএবং প্রকারের মানটির (যদি এটির শূন্যতা এটি ডিফল্ট হয় তবে) এর বিরুদ্ধে পরীক্ষা করে বুলেয়ান থেকে মুক্তি পান ।
ফেলিক্স কে।

291

সি # 7 দিয়ে , যা ভিজ্যুয়াল স্টুডিও 2017 (প্রেরণ 15. *) এর সাথে প্রেরণ করা হয়েছে, আপনি caseবিবৃতিতে ধরণগুলি (প্যাটার্ন ম্যাচিং) ব্যবহার করতে সক্ষম হবেন :

switch(shape)
{
    case Circle c:
        WriteLine($"circle with radius {c.Radius}");
        break;
    case Rectangle s when (s.Length == s.Height):
        WriteLine($"{s.Length} x {s.Height} square");
        break;
    case Rectangle r:
        WriteLine($"{r.Length} x {r.Height} rectangle");
        break;
    default:
        WriteLine("<unknown shape>");
        break;
    case null:
        throw new ArgumentNullException(nameof(shape));
}

সি # 6 এর সাহায্যে আপনি নামের ( অপারেটর) অপারেটরটির সাথে একটি সুইচ স্টেটমেন্ট ব্যবহার করতে পারেন (ধন্যবাদ @ জোয়ি অ্যাডামস):

switch(o.GetType().Name) {
    case nameof(AType):
        break;
    case nameof(BType):
        break;
}

সি # 5 এবং এর আগে, আপনি একটি স্যুইচ স্টেটমেন্ট ব্যবহার করতে পারেন, তবে আপনাকে টাইপ নামযুক্ত একটি ম্যাজিক স্ট্রিং ব্যবহার করতে হবে ... যা বিশেষত রিফ্যাক্টর বান্ধব নয় (ধন্যবাদ @ নোকিফিউশন)

switch(o.GetType().Name) {
  case "AType":
    break;
}

1
কেস টাইপফ (স্ট্রিং) দিয়ে এই কাজ করে? নাম: ... বা এটি ভালুটাইপের সাথে থাকতে হবে?
টোমর ডাব্লু

3
উদ্বিগ্নতা এটি ভাঙ্গতে পারে
কনরাড মোরাউস্কি

6
@ নোকফিউশন: এটি, যদি না আপনি চকচকে নতুন nameof()অপারেটর ব্যবহার করেন ।
জোয়ে অ্যাডামস

21
এই উত্তরটি আমি পছন্দ করি না কারণ নামফ (নেমস্পেসএ.ক্লাসসি) == নামফ (নেমস্পেসবি.ক্লাসসি) সত্য।
ischas

7
(সি # 7) আপনি যদি অবজেক্টটিতে অ্যাক্সেস না প্রয়োজন হয় তবে আপনি আন্ডারস্কোরও ব্যবহার করতে পারেন:case UnauthorizedException _:
এসফ এস

101

একটি বিকল্প থেকে একটি অভিধান আছে হয় Typeথেকে Action(অথবা অন্য কোনো প্রতিনিধি)। প্রকারের ভিত্তিতে ক্রিয়াটি দেখুন এবং তারপরে এটি সম্পাদন করুন। আমি এখন আগে কারখানার জন্য এটি ব্যবহার করেছি।


31
গৌণ দ্রষ্টব্য: 1: 1 ম্যাচের জন্য ভাল তবে উত্তরাধিকার এবং / অথবা ইন্টারফেসগুলির সাথে ব্যথা হতে পারে - বিশেষত অর্ডারটি কোনও অভিধান দিয়ে সংরক্ষণ করার গ্যারান্টিযুক্ত নয় as তবে এখনও, আমি এটি বেশ কয়েকটি স্থানেই করি ;-p সুতরাং +1
মার্ক গ্র্যাভেল

@ মার্ক: এই দৃষ্টান্তে উত্তরাধিকার বা ইন্টারফেসগুলি কীভাবে ভেঙে যাবে? কীটি ধরে নিলে এটি একটি প্রকার, এবং ক্রিয়াটি একটি পদ্ধতি, তারপরে উত্তরাধিকার বা ইন্টারফেসগুলি আসলে ডান থিং (টিএম) কে জোর করা উচিত, যতদূর আমি বলতে পারি। আমি অবশ্যই একাধিক ক্রিয়া এবং আদেশের অভাব নিয়ে বিষয়টি বুঝতে পারি।
হার্পার শেলবি

2
আমি অতীতে এই কৌশলটি প্রচুর ব্যবহার করেছি, সাধারণত কোনও আইওসি কনটেইনারে যাওয়ার আগে
ক্রিস খাল

4
এই কৌশলটি উত্তরাধিকার এবং ইন্টারফেসগুলির জন্য বিচ্ছিন্ন হয়ে পড়ে কারণ আপনি যে জিনিসটি যাচাই করছেন এবং যে ডেলিগেটকে আপনি কল করছেন তার মধ্যে আপনার একের মধ্যে একটি চিঠিপত্রের প্রয়োজন। অভিধানের মধ্যে কোন বস্তুর একাধিক ইন্টারফেসের সন্ধান করার চেষ্টা করা উচিত?
রবার্ট রসনি

5
আপনি যদি এই উদ্দেশ্যে বিশেষত একটি অভিধান বানাচ্ছেন তবে আপনি কী প্রকারের মানটি ফেরত দিতে সূচককে ওভারলোড করতে পারেন, বা যদি তার সুপারক্লাসটি অনুপস্থিত থাকে তবে যদি সেই সুপারক্লাস ইত্যাদি অনুপস্থিত থাকে, যতক্ষণ না কিছুই থাকে না।
এরিক ফোর্বস

49

আমার মাথার পিছনে জ্যারেডপারের উত্তর সহ , আমি তাঁর TypeSwitchশ্রেণীর একটি বৈকল্পিক লিখেছি যা একটি সুন্দর সিনট্যাক্সের জন্য টাইপ অনুক্রম ব্যবহার করে:

class A { string Name { get; } }
class B : A { string LongName { get; } }
class C : A { string FullName { get; } }
class X { public string ToString(IFormatProvider provider); }
class Y { public string GetIdentifier(); }

public string GetName(object value)
{
    string name = null;
    TypeSwitch.On(value)
        .Case((C x) => name = x.FullName)
        .Case((B x) => name = x.LongName)
        .Case((A x) => name = x.Name)
        .Case((X x) => name = x.ToString(CultureInfo.CurrentCulture))
        .Case((Y x) => name = x.GetIdentifier())
        .Default((x) => name = x.ToString());
    return name;
}

নোট করুন যে Case()পদ্ধতিগুলির ক্রমটি গুরুত্বপূর্ণ।


আমার TypeSwitchক্লাসের জন্য পূর্ণ এবং মন্তব্য কোডটি পান । এটি একটি কাজের সংক্ষিপ্ত সংস্করণ:

public static class TypeSwitch
{
    public static Switch<TSource> On<TSource>(TSource value)
    {
        return new Switch<TSource>(value);
    }

    public sealed class Switch<TSource>
    {
        private readonly TSource value;
        private bool handled = false;

        internal Switch(TSource value)
        {
            this.value = value;
        }

        public Switch<TSource> Case<TTarget>(Action<TTarget> action)
            where TTarget : TSource
        {
            if (!this.handled && this.value is TTarget)
            {
                action((TTarget) this.value);
                this.handled = true;
            }
            return this;
        }

        public void Default(Action<TSource> action)
        {
            if (!this.handled)
                action(this.value);
        }
    }
}

একটি ভাল সমাধান বলে মনে হচ্ছে এবং এটি সম্পর্কে আপনার আর কী বলতে হবে তা দেখতে চেয়েছিলেন তবে ব্লগটি মারা গেছে।
ওয়েস গ্রান্ট

1
ডরান, আপনি ঠিক বলেছেন। এক ঘন্টা থেকে আমার ওয়েবহোস্টে কিছু সমস্যা হচ্ছে। তারা এটিতে কাজ করছে। আমার ব্লগের পোস্টটি এখানে মূলত উত্তর হিসাবে একই, তবে সম্পূর্ণ উত্স কোডের লিঙ্ক সহ।
ড্যানিয়েল এএ পেলসমেকার

1
যদি এটি একটি সহজ "ক্রিয়ামূলক" স্যুইচটিতে বন্ধনীগুলি বন্ধ করে দেয় তবে এটি কীভাবে ভালোবাসুন। চমৎকার কাজ!
জেমস হোয়াইট

2
এছাড়াও আপনি প্রাথমিক ক্ষেত্রে জন্য একটি এক্সটেনশন পদ্ধতি যুক্ত করতে পারেন: public static Switch<TSource> Case<TSource, TTarget>(this TSource value, Action<TTarget> action) where TTarget : TSource। এটি আপনাকে বলতে দেয়value.Case((C x) ...
জোয়ি অ্যাডামস 21

1
@ জোয়্যা অ্যাডামস: আমি আপনার সর্বশেষ পরামর্শটি সহ কিছু ছোট উন্নতিও অন্তর্ভুক্ত করেছি। যাইহোক, আমি সিনট্যাক্সটি একই রাখছি।
ড্যানিয়েল এএ পেলসেমেকার

14

একটি সুপারক্লাস (এস) তৈরি করুন এবং এ এবং বি এর উত্তরাধিকারী করুন। তারপরে এস-তে একটি বিমূর্ত পদ্ধতি ঘোষণা করুন যা প্রতিটি সাবক্লাসটি প্রয়োগ করতে হবে।

এটি "ফু" পদ্ধতিটি করাতে এর স্বাক্ষরটি ফু (এস ও) এও পরিবর্তন করা যায়, এটি নিরাপদ টাইপ করে তোলে এবং আপনার এই কুৎসিত ব্যতিক্রম ছোঁড়ার দরকার নেই।


সত্য ব্রুনো, তবে প্রশ্নটি সেটির প্রস্তাব দেয় না। আপনি পাবলো যদিও আপনার উত্তরে এটি অন্তর্ভুক্ত করতে পারেন।
দানা দ্য সনে

প্রশ্ন থেকে আমি মনে করি A এবং B যথেষ্ট জেনেরিক যে তারা A = স্ট্রিং হতে পারে; বি = তালিকা
<<<

13

আপনি সি # 7 বা তদূর্ধ্বের মধ্যে প্যাটার্ন মিলটি ব্যবহার করতে পারেন:

switch (foo.GetType())
{
    case var type when type == typeof(Player):
        break;
    case var type when type == typeof(Address):
        break;
    case var type when type == typeof(Department):
        break;
    case var type when type == typeof(ContactType):
        break;
    default:
        break;
}

এই এক জন্য আপনাকে ধন্যবাদ! সাবক্লাসগুলি সনাক্ত করতেও এটি ব্যবহার করা যেতে পারে: যদি (এটি.আলে.প্লেম্পটেন্ট প্যারেন্ট.গেটটাইপ ()। ইসসব্লাক্লাসফ (টাইপফ (র‌্যাডগ্রিডভিউ))) এ পরিবর্তিত হতে পারে: স্যুইচ (this.TemplatedParent.GetType ()) কেস subRadGridView.IsSubclassOf (যখন সাবস্ক্ল্যাশফ্লুফ টাইপফ (র‌্যাডগ্রিডভিউ):
ফ্লেমিং বোনডে কেন্তেভড

আপনি এটা ভুল করছেন. সার্জ ইন্টার্নের উত্তর দেখুন এবং
লিসকোভ

8

আপনার সত্যই আপনার পদ্ধতিটি ওভারলোড করা উচিত, নিজেই অসন্তুষ্টির চেষ্টা না করে। এখনও অবধি বেশিরভাগ উত্তর ভবিষ্যতের সাবক্লাসগুলিকে বিবেচনায় নেয় না, যা পরবর্তী সময়ে সত্যই ভয়ানক রক্ষণাবেক্ষণের সমস্যার কারণ হতে পারে।


3
ওভারলোডের রেজোলিউশনটি স্থিতিশীলভাবে নির্ধারিত হয় যাতে কেবল এগুলি কার্যকর হয় না।
নিউট্রিনো

@ নিউট্রিনো: প্রশ্নের মধ্যে এমন কিছুই নেই যা নির্দেশ করে যে সংকলন করার সময় টাইপটি জানা যায়নি। এবং যদি এটি হয় তবে ওপির মূল কোড উদাহরণটি দিয়ে একটি ওভারলোড অন্য যে কোনও বিকল্পের চেয়ে বেশি বোঝায়।
পিটার ডুনিহো

আমি মনে করি যে প্রকারটি নির্ধারণের জন্য তিনি 'যদি' বা 'সুইচ' বিবৃতিটি ব্যবহার করার চেষ্টা করছেন তবে এটি একটি স্পষ্ট ইঙ্গিত যে টাইপটি সংকলন করার সময় জানা ছিল না।
নিউট্রিনো

@ নিউট্রিনো, আমি আপনাকে মনে রেখেছি যে, সের্গেই বেরেজোভস্কিই উল্লেখ করেছেন যে, সি # তে গতিশীল কীওয়ার্ড রয়েছে যা একটি প্রকারের প্রতিনিধিত্ব করে যা গতিশীলভাবে সমাধান করতে হবে (রানটাইম সময়ে, সংকলন-সময়ের চেয়ে)।
ডেভিড কান্নিজো

8

আপনি যদি সি # 4 ব্যবহার করছিলেন তবে একটি আকর্ষণীয় বিকল্প অর্জন করতে আপনি নতুন গতিশীল কার্যকারিতাটি ব্যবহার করতে পারেন। আমি বলছি না এটি আরও ভাল, বাস্তবে এটি খুব ধীর হতে পারে বলে খুব সম্ভবত মনে হয় তবে এটির একটি নির্দিষ্ট কমনীয়তা আছে।

class Thing
{

  void Foo(A a)
  {
     a.Hop();
  }

  void Foo(B b)
  {
     b.Skip();
  }

}

এবং ব্যবহার:

object aOrB = Get_AOrB();
Thing t = GetThing();
((dynamic)t).Foo(aorB);

এটি কাজ করার কারণটি হ'ল একটি সি # 4 গতিশীল পদ্ধতির অনুরোধটির সময়কালের পরিবর্তে রান-টাইমে ওভারলোডগুলি সমাধান হয়েছে। আমি এই ধারণাটি সম্পর্কে বেশ সম্প্রতি লিখেছি । আবার, আমি কেবল আবার বলতে চাই যে এটি সম্ভবত অন্যান্য সমস্ত পরামর্শের চেয়ে খারাপ সম্পাদন করে, আমি এটিকে কেবল কৌতূহল হিসাবে দিচ্ছি।


1
আমি আজ একই ধারণা ছিল। টাইপটির নামটি স্যুইচ করার চেয়ে এটি প্রায় 3 গুণ ধীর। অবশ্যই ধীর গতির তুলনায় (60০,০০,০০০ কল, মাত্র ৪ সেকেন্ডের জন্য) the এবং কোডটি এত বেশি পঠনযোগ্য এটির পক্ষে এটি ভাল।
ড্যারিল

8

হ্যাঁ, সি # 7 এর জন্য ধন্যবাদ যা অর্জন করা যায়। এটি কীভাবে সম্পন্ন হয়েছে ( এক্সপ্রেশন প্যাটার্ন ব্যবহার করে ) এখানে:

switch (o)
{
    case A a:
        a.Hop();
        break;
    case B b:
        b.Skip();
        break;
    case C _: 
        return new ArgumentException("Type C will be supported in the next version");
    default:
        return new ArgumentException("Unexpected type: " + o.GetType());
}


7

অন্তর্নির্মিত প্রকারের জন্য, আপনি টাইপকোড গণনা ব্যবহার করতে পারেন। দয়া করে নোট করুন যে গেটটাইপ () ধরণের ধীর, তবে বেশিরভাগ পরিস্থিতিতে সম্ভবত প্রাসঙ্গিক নয়।

switch (Type.GetTypeCode(someObject.GetType()))
{
    case TypeCode.Boolean:
        break;
    case TypeCode.Byte:
        break;
    case TypeCode.Char:
        break;
}

কাস্টম ধরণের জন্য, আপনি নিজের স্বীকৃতি তৈরি করতে পারেন এবং হয় একটি ইন্টারফেস বা বিমূর্ত সম্পত্তি বা পদ্ধতি সহ একটি বেস শ্রেণি ...

সম্পত্তির বিমূর্ত শ্রেণি প্রয়োগ

public enum FooTypes { FooFighter, AbbreviatedFool, Fubar, Fugu };
public abstract class Foo
{
    public abstract FooTypes FooType { get; }
}
public class FooFighter : Foo
{
    public override FooTypes FooType { get { return FooTypes.FooFighter; } }
}

পদ্ধতির বিমূর্ত শ্রেণি প্রয়োগ

public enum FooTypes { FooFighter, AbbreviatedFool, Fubar, Fugu };
public abstract class Foo
{
    public abstract FooTypes GetFooType();
}
public class FooFighter : Foo
{
    public override FooTypes GetFooType() { return FooTypes.FooFighter; }
}

সম্পত্তির ইন্টারফেস বাস্তবায়ন

public enum FooTypes { FooFighter, AbbreviatedFool, Fubar, Fugu };
public interface IFooType
{
    FooTypes FooType { get; }
}
public class FooFighter : IFooType
{
    public FooTypes FooType { get { return FooTypes.FooFighter; } }
}

পদ্ধতির ইন্টারফেস বাস্তবায়ন

public enum FooTypes { FooFighter, AbbreviatedFool, Fubar, Fugu };
public interface IFooType
{
    FooTypes GetFooType();
}
public class FooFighter : IFooType
{
    public FooTypes GetFooType() { return FooTypes.FooFighter; }
}

আমার এক সহকর্মী আমাকে এই সম্পর্কে কেবল বলেছেন: এটির সুবিধা রয়েছে যে আপনি এটি আক্ষরিক যে কোনও ধরণের অবজেক্টের জন্য ব্যবহার করতে পারবেন, কেবলমাত্র আপনি যা সংজ্ঞায়িত করেছেন তা নয়। এটি কিছুটা বড় এবং ধীর হওয়ার অসুবিধা রয়েছে।

প্রথমে এর মতো একটি স্ট্যাটিক শ্রেণি সংজ্ঞায়িত করুন:

public static class TypeEnumerator
{
    public class TypeEnumeratorException : Exception
    {
        public Type unknownType { get; private set; }
        public TypeEnumeratorException(Type unknownType) : base()
        {
            this.unknownType = unknownType;
        }
    }
    public enum TypeEnumeratorTypes { _int, _string, _Foo, _TcpClient, };
    private static Dictionary<Type, TypeEnumeratorTypes> typeDict;
    static TypeEnumerator()
    {
        typeDict = new Dictionary<Type, TypeEnumeratorTypes>();
        typeDict[typeof(int)] = TypeEnumeratorTypes._int;
        typeDict[typeof(string)] = TypeEnumeratorTypes._string;
        typeDict[typeof(Foo)] = TypeEnumeratorTypes._Foo;
        typeDict[typeof(System.Net.Sockets.TcpClient)] = TypeEnumeratorTypes._TcpClient;
    }
    /// <summary>
    /// Throws NullReferenceException and TypeEnumeratorException</summary>
    /// <exception cref="System.NullReferenceException">NullReferenceException</exception>
    /// <exception cref="MyProject.TypeEnumerator.TypeEnumeratorException">TypeEnumeratorException</exception>
    public static TypeEnumeratorTypes EnumerateType(object theObject)
    {
        try
        {
            return typeDict[theObject.GetType()];
        }
        catch (KeyNotFoundException)
        {
            throw new TypeEnumeratorException(theObject.GetType());
        }
    }
}

এবং তারপরে আপনি এটি ব্যবহার করতে পারেন:

switch (TypeEnumerator.EnumerateType(someObject))
{
    case TypeEnumerator.TypeEnumeratorTypes._int:
        break;
    case TypeEnumerator.TypeEnumeratorTypes._string:
        break;
}

টাইপকোড () - আদিম প্রকারের জন্য বৈকল্পিক যুক্ত করার জন্য ধন্যবাদ, কারণ এমনকি সি # 7.0 - রূপগুলিও তাদের সাথে কাজ করে না (স্পষ্টতই নামও দেয় না)
ওলে অ্যালবারস

6

আমি সুইচটিকে আরও বেশি পঠনযোগ্য করে তোলার জন্য ভার্টলিংকের অন্তর্নিহিত টাইপিংয়ের ব্যবহার পছন্দ করেছি , তবে আমার পছন্দ হয়নি যে প্রারম্ভিক আউট সম্ভব নয়, এবং আমরা বরাদ্দ করছি। এবার পারফটিকে একটু উপরে ফেলা যাক।

public static class TypeSwitch
{
    public static void On<TV, T1>(TV value, Action<T1> action1)
        where T1 : TV
    {
        if (value is T1) action1((T1)value);
    }

    public static void On<TV, T1, T2>(TV value, Action<T1> action1, Action<T2> action2)
        where T1 : TV where T2 : TV
    {
        if (value is T1) action1((T1)value);
        else if (value is T2) action2((T2)value);
    }

    public static void On<TV, T1, T2, T3>(TV value, Action<T1> action1, Action<T2> action2, Action<T3> action3)
        where T1 : TV where T2 : TV where T3 : TV
    {
        if (value is T1) action1((T1)value);
        else if (value is T2) action2((T2)value);
        else if (value is T3) action3((T3)value);
    }

    // ... etc.
}

ঠিক আছে, এটি আমার আঙ্গুলগুলিকে আঘাত করে। আসুন টি -4 এ এটি করুন:

<#@ template debug="false" hostSpecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ Assembly Name="System.Core.dll" #>
<#@ import namespace="System.Linq" #> 
<#@ import namespace="System.IO" #> 
<#
    string GenWarning = "// THIS FILE IS GENERATED FROM " + Path.GetFileName(Host.TemplateFile) + " - ANY HAND EDITS WILL BE LOST!";
    const int MaxCases = 15;
#>
<#=GenWarning#>

using System;

public static class TypeSwitch
{
<# for(int icase = 1; icase <= MaxCases; ++icase) {
    var types = string.Join(", ", Enumerable.Range(1, icase).Select(i => "T" + i));
    var actions = string.Join(", ", Enumerable.Range(1, icase).Select(i => string.Format("Action<T{0}> action{0}", i)));
    var wheres = string.Join(" ", Enumerable.Range(1, icase).Select(i => string.Format("where T{0} : TV", i)));
#>
    <#=GenWarning#>

    public static void On<TV, <#=types#>>(TV value, <#=actions#>)
        <#=wheres#>
    {
        if (value is T1) action1((T1)value);
<# for(int i = 2; i <= icase; ++i) { #>
        else if (value is T<#=i#>) action<#=i#>((T<#=i#>)value);
<#}#>
    }

<#}#>
    <#=GenWarning#>
}

ভার্টলিংকের উদাহরণটি সামঞ্জস্য করুন:

TypeSwitch.On(operand,
    (C x) => name = x.FullName,
    (B x) => name = x.LongName,
    (A x) => name = x.Name,
    (X x) => name = x.ToString(CultureInfo.CurrentCulture),
    (Y x) => name = x.GetIdentifier(),
    (object x) => name = x.ToString());

পাঠযোগ্য এবং দ্রুত। এখন, যেহেতু প্রত্যেকে তাদের উত্তরগুলিতে ইঙ্গিত করে চলেছে এবং এই প্রশ্নের প্রকৃতি দেওয়া হয়েছে, টাইপ মেলানোর ক্ষেত্রে ক্রমটি গুরুত্বপূর্ণ। অতএব:

  • প্রথমে পাতার ধরণ রাখুন, পরে বেস প্রকারগুলি।
  • পিয়ারের ধরণের জন্য, পারফেক্ট সর্বাধিক করতে প্রথমে আরও বেশি সম্ভাবনা মেলে put
  • এটি সূচিত করে যে কোনও বিশেষ ডিফল্ট মামলার প্রয়োজন নেই। পরিবর্তে, কেবল ল্যাম্বডায় বেস-সর্বাধিক প্রকারটি ব্যবহার করুন এবং এটি শেষ রাখুন।

5

প্রদত্ত উত্তরাধিকার কোনও বস্তুকে একাধিক প্রকার হিসাবে স্বীকৃতি দিতে সহায়তা করে, আমার মনে হয় একটি স্যুইচ খারাপ প্রতিকূলতার দিকে নিয়ে যেতে পারে। উদাহরণ স্বরূপ:

মামলা 1

{
  string s = "a";
  if (s is string) Print("Foo");
  else if (s is object) Print("Bar");
}

মামলা 2

{
  string s = "a";
  if (s is object) Print("Foo");
  else if (s is string) Print("Bar");
}

কারণ s একটি স্ট্রিং এবং একটি অবজেক্ট। আমি মনে করি আপনি যখন একটি লিখেন তখন আপনি switch(foo)ফু ও থেকে একটিতে এবং caseবিবৃতিতে কেবল একটির সাথে মিলবে বলে আশা করেন । প্রকারভেদে একটি স্যুইচ দিয়ে আপনি যে ক্রমে আপনার কেস স্টেটমেন্টগুলি লিখেছেন তা সম্ভবত পুরো সুইচ স্টেটমেন্টের ফলাফল পরিবর্তন করতে পারে। আমি মনে করি এটি ভুল হবে।

আপনি একটি "টাইপউইচ" স্টেটমেন্টের প্রকারের সংকলক-চেকের কথা চিন্তা করতে পারেন, পরীক্ষা করা হয়েছে যে গণনা করা প্রকারগুলি একে অপরের থেকে উত্তরাধিকারসূত্রে আসে না। যদিও এর অস্তিত্ব নেই।

foo is Tএকই মত না foo.GetType() == typeof(T)!!



4

আরেকটি উপায় হ'ল একটি ইন্টারফেস আইটিহিং সংজ্ঞায়িত করা এবং তারপরে এটি উভয় শ্রেণিতে প্রয়োগ করা এখানে স্নিপেট:

public interface IThing
{
    void Move();
}

public class ThingA : IThing
{
    public void Move()
    {
        Hop();
    }

    public void Hop(){  
        //Implementation of Hop 
    }

}

public class ThingA : IThing
{
    public void Move()
    {
        Skip();
    }

    public void Skip(){ 
        //Implementation of Skip    
    }

}

public class Foo
{
    static void Main(String[] args)
    {

    }

    private void Foo(IThing a)
    {
        a.Move();
    }
}

4

সি প্রতি # 7.0 স্পেসিফিকেশন হিসাবে, আপনি একটি স্থানীয় পরিবর্তনশীল একটি scoped ডিক্লেয়ার করতে পারেন caseএকটি এর switch:

object a = "Hello world";
switch (a)
{
    case string myString:
        // The variable 'a' is a string!
        break;
    case int myInt:
        // The variable 'a' is an int!
        break;
    case Foo myFoo:
        // The variable 'a' is of type Foo!
        break;
}

এ জাতীয় কাজটি করার এটি সর্বোত্তম উপায় কারণ এটিতে কেবল ingালাই এবং ধাক্কা-অন-স্ট্যাক অপারেশন জড়িত, যা দোভাষী কেবল বিটওয়াইজ অপারেশন এবং booleanশর্তের পরে চালাতে পারে এমন দ্রুততম ক্রিয়াকলাপ ।

এটার সাথে তুলনা করছি ক Dictionary<K, V> , এখানে মেমরির ব্যবহারের খুব কম ব্যবহার: একটি অভিধান ধারণের জন্য র‌্যামে আরও স্থান প্রয়োজন হয় এবং সিপিইউ দ্বারা দুটি অ্যারে তৈরি করার জন্য একটি কম্পিউটারের আরও বেশি প্রয়োজন (একটিতে কীগুলির জন্য এবং অন্যটি মানের জন্য) এবং কীগুলি রাখার জন্য হ্যাশ কোড সংগ্রহ করা হয় নিজ নিজ কীগুলির মান values

সুতরাং, জন্য যতদূর আমি জানি, আমি বিশ্বাস করি না যে একটি দ্রুত উপায় যদি না আপনি শুধু একটি ব্যবহার করতে চান অস্তিত্ব পারে if- then- elseব্লক isনিম্নরূপ অপারেটর:

object a = "Hello world";
if (a is string)
{
    // The variable 'a' is a string!
} else if (a is int)
{
    // The variable 'a' is an int!
} // etc.

3

আপনি অতিরিক্ত লোড পদ্ধতি তৈরি করতে পারেন:

void Foo(A a) 
{ 
    a.Hop(); 
}

void Foo(B b) 
{ 
    b.Skip(); 
}

void Foo(object o) 
{ 
    throw new ArgumentException("Unexpected type: " + o.GetType()); 
}

dynamicস্ট্যাটিক টাইপ চেকিং বাইপাস করতে টাইপ করতে যুক্তিটি নিক্ষেপ করুন :

Foo((dynamic)something);

3

প্যাটার্ন মিলের সি # 8 বর্ধনের ফলে এটি এটি করা সম্ভব করেছে। কিছু ক্ষেত্রে এটি কাজ করে এবং আরও সংক্ষিপ্ত করে।

        public Animal Animal { get; set; }
        ...
        var animalName = Animal switch
        {
            Cat cat => "Tom",
            Mouse mouse => "Jerry",
            _ => "unknown"
        };

2

আপনি Discriminated Unionsযেগুলি এফ # এর একটি ভাষা বৈশিষ্ট্য হিসাবে সন্ধান করছেন, তবে আপনি আমার তৈরি একটি লাইব্রেরি ওয়ানওফ ব্যবহার করে একই রকম প্রভাব অর্জন করতে পারেন

https://github.com/mcintyre321/OneOf

switch(এবং ifএবং exceptions as control flow) এর সবচেয়ে বড় সুবিধাটি হ'ল এটি সংকলন-সময় নিরাপদ - কোনও ডিফল্ট হ্যান্ডলার বা এর মধ্যে পড়ে না

void Foo(OneOf<A, B> o)
{
    o.Switch(
        a => a.Hop(),
        b => b.Skip()
    );
}

যদি আপনি ও তে কোনও তৃতীয় আইটেম যোগ করেন তবে আপনি একটি সংকলক ত্রুটি পাবেন কারণ আপনাকে স্যুইচ কলের ভিতরে একটি হ্যান্ডলার ফানক যুক্ত করতে হবে।

আপনি .Matchকোনও বিবৃতি কার্যকর করার পরিবর্তে কোনও মান ফেরত দেওয়ার জন্যও এটি করতে পারেন :

double Area(OneOf<Square, Circle> o)
{
    return o.Match(
        square => square.Length * square.Length,
        circle => Math.PI * circle.Radius * circle.Radius
    );
}

2

একটি ইন্টারফেস তৈরি করুন IFooable, তারপরে একটি সাধারণ পদ্ধতি বাস্তবায়নের জন্য আপনার Aএবং Bক্লাসগুলি তৈরি করুন , যার ফলে আপনি চান সেই একই পদ্ধতিটিকে কল করুন:

interface IFooable
{
    public void Foo();
}

class A : IFooable
{
    //other methods ...

    public void Foo()
    {
        this.Hop();
    }
}

class B : IFooable
{
    //other methods ...

    public void Foo()
    {
        this.Skip();
    }
}

class ProcessingClass
{
    public void Foo(object o)
    {
        if (o == null)
            throw new NullRefferenceException("Null reference", "o");

        IFooable f = o as IFooable;
        if (f != null)
        {
            f.Foo();
        }
        else
        {
            throw new ArgumentException("Unexpected type: " + o.GetType());
        }
    }
}

দ্রষ্টব্য, যে asপরিবর্তে প্রথমে পরীক্ষা করে isতারপরে কাস্টিং ব্যবহার করা আরও ভাল better সুতরাং আপনি 2 টি কাস্ট তৈরি করেন, সুতরাং এটি আরও ব্যয়বহুল।


2

এই জাতীয় ক্ষেত্রে আমি সাধারণত পূর্বাভাস এবং ক্রিয়াগুলির একটি তালিকা দিয়ে শেষ করি। এই লাইন বরাবর কিছু:

class Mine {
    static List<Func<object, bool>> predicates;
    static List<Action<object>> actions;

    static Mine() {
        AddAction<A>(o => o.Hop());
        AddAction<B>(o => o.Skip());
    }

    static void AddAction<T>(Action<T> action) {
        predicates.Add(o => o is T);
        actions.Add(o => action((T)o);
    }

    static void RunAction(object o) {
        for (int i=0; o < predicates.Count; i++) {
            if (predicates[i](o)) {
                actions[i](o);
                break;
            }
        }
    }

    void Foo(object o) {
        RunAction(o);
    }
}

2

এফ # বৈশিষ্ট্যগুলিতে সরবরাহ করা কয়েকটি উত্তর বিকল্পগুলির তুলনা করার পরে, আমি টাইপ-ভিত্তিক স্যুইচিংয়ের জন্য আরও ভাল সহায়তার জন্য এফ # আবিষ্কার করেছি (যদিও আমি এখনও সি # তে আঁকড়ে রয়েছি)।
আপনি এখানে এবং এখানে দেখতে চাইতে পারেন ।


2
<এফ # এর জন্য এখানে প্লাগ প্রবেশ করুন>
ওভারলর্ড জুরগ

1

আমি যে নাম এবং পদ্ধতির নাম যা আপনার সুইচটির জন্য অর্থবোধ করবে তার সাথে আমি একটি ইন্টারফেস তৈরি করব, আসুন যথাক্রমে তাদের কল করুন: IDoableযা বাস্তবায়ন করতে বলে void Do()

public interface IDoable
{
    void Do();
}

public class A : IDoable
{
    public void Hop() 
    {
        // ...
    }

    public void Do()
    {
        Hop();
    }
}

public class B : IDoable
{
    public void Skip() 
    {
        // ...
    }

    public void Do()
    {
        Skip();
    }
}

এবং পদ্ধতিটি নিম্নরূপে পরিবর্তন করুন:

void Foo<T>(T obj)
    where T : IDoable
{
    // ...
    obj.Do();
    // ...
}

কমপক্ষে তার সাথে আপনি সংকলন-সময় নিরাপদ এবং আমার সন্দেহ যে পারফরম্যান্স-ভিত্তিতে এটি রানটাইম টাইপ পরীক্ষা করার চেয়ে ভাল।


1

সি # 8 এর পরে আপনি নতুন স্যুইচ দিয়ে এটি আরও সংক্ষিপ্ত করতে পারেন। এবং বাতিল করা বিকল্পের ব্যবহারের সাথে _ যখন আপনার প্রয়োজন হয় না তখন আপনি ইনসেসারি ভেরিয়েবলগুলি এড়াতে পারবেন:

        return document switch {
            Invoice _ => "Is Invoice",
            ShippingList _ => "Is Shipping List",
            _ => "Unknown"
        };

চালান এবং শিপিংলিস্ট ক্লাস এবং নথি হ'ল একটি বস্তু যা সেগুলির মধ্যে দুটি হতে পারে।


0

ক্লাসের নামের সাথে ক্রিয়া করার একটি হ্যাশ সম্পর্কে আমি জনের সাথে একমত agree আপনি যদি আপনার প্যাটার্নটি রাখেন তবে আপনি পরিবর্তে "হিসাবে" কনস্ট্রাক্ট ব্যবহার করে বিবেচনা করতে পারেন:

A a = o as A;
if (a != null) {
    a.Hop();
    return;
}
B b = o as B;
if (b != null) {
    b.Skip();
    return;
}
throw new ArgumentException("...");

পার্থক্যটি হ'ল আপনি যখন প্যাটারটি ব্যবহার করেন যদি (foo বার হয়) {((বার) foo) ;অ্যাকশন (); } আপনি টাইপ ingালাই দুইবার করছেন। এখন সম্ভবত সংকলকটি অনুকূলিত হবে এবং কেবল একবার সেই কাজটি করবে - তবে আমি এটির উপর নির্ভর করব না।


1
আমি সত্যিই একাধিক প্রস্থান পয়েন্টগুলি (রিটার্ন) পছন্দ করি না তবে আপনি যদি এটির সাথে আটকে থাকতে চান তবে শুরুতে "if (o == নাল) নিক্ষেপ" যুক্ত করুন, পরবর্তীকালে আপনি জানতে পারবেন না castালাই ব্যর্থ হয়েছে কিনা, বা বস্তু নাল ছিল।
সানি মাইলেনভ

0

পাবলো যেমন পরামর্শ দিয়েছেন, ইন্টারফেসের পদ্ধতির এটি হ্যান্ডেল করার জন্য প্রায়শই সঠিক জিনিস। স্যুইচটি সত্যিই ব্যবহার করতে, অন্য বিকল্প হ'ল আপনার ক্লাসে আপনার প্রকারকে বোঝায় একটি কাস্টম এনাম।

enum ObjectType { A, B, Default }

interface IIdentifiable
{
    ObjectType Type { get; };
}
class A : IIdentifiable
{
    public ObjectType Type { get { return ObjectType.A; } }
}

class B : IIdentifiable
{
    public ObjectType Type { get { return ObjectType.B; } }
}

void Foo(IIdentifiable o)
{
    switch (o.Type)
    {
        case ObjectType.A:
        case ObjectType.B:
        //......
    }
}

এটি বিসিএলেও এক ধরণের বাস্তবায়িত। একটি উদাহরণ মেম্বার ইনফো , মেমবারটাইপস , অন্যটি GetTypeCodeআদিম ধরণের জন্য যেমন:

void Foo(object o)
{
    switch (Type.GetTypeCode(o.GetType())) // for IConvertible, just o.GetTypeCode()
    {
        case TypeCode.Int16:
        case TypeCode.Int32:
        //etc ......
    }
}

0

এটি একটি বিকল্প উত্তর যা জারেডপাড় এবং ভার্টলিঙ্ক উত্তরগুলির সাথে অবদানগুলি মিশ্রিত করে, নিম্নলিখিত সীমাবদ্ধতার সাথে:

  • স্যুইচ নির্মাণটি একটি ফাংশন হিসাবে আচরণ করে এবং কেসগুলির পরামিতি হিসাবে ফাংশনগুলি গ্রহণ করে
  • এটি সঠিকভাবে নির্মিত হয়েছে তা নিশ্চিত করে এবং সর্বদা একটি ডিফল্ট ফাংশন উপস্থিত থাকে
  • এটি প্রথম ম্যাচের পরে ফিরে আসে (জ্যারেডপার উত্তরের জন্য সত্য, ভার্টলিঙ্ক একের পক্ষে সত্য নয়)।

ব্যবহার:

 var result = 
   TSwitch<string>
     .On(val)
     .Case((string x) => "is a string")
     .Case((long x) => "is a long")
     .Default(_ => "what is it?");

কোড:

public class TSwitch<TResult>
{
    class CaseInfo<T>
    {
        public Type Target { get; set; }
        public Func<object, T> Func { get; set; }
    }

    private object _source;
    private List<CaseInfo<TResult>> _cases;

    public static TSwitch<TResult> On(object source)
    {
        return new TSwitch<TResult> { 
            _source = source,
            _cases = new List<CaseInfo<TResult>>()
        };
    }

    public TResult Default(Func<object, TResult> defaultFunc)
    {
        var srcType = _source.GetType();
       foreach (var entry in _cases)
            if (entry.Target.IsAssignableFrom(srcType))
                return entry.Func(_source);

        return defaultFunc(_source);
    }

    public TSwitch<TResult> Case<TSource>(Func<TSource, TResult> func)
    {
        _cases.Add(new CaseInfo<TResult>
        {
            Func = x => func((TSource)x),
            Target = typeof(TSource)
        });
        return this;
    }
}

0

হ্যাঁ - শ্রেণি বা কাঠামোর সাথে মিলতে কেবল সি # 7 থেকে সামান্য অদ্ভুতভাবে নামযুক্ত "প্যাটার্ন ম্যাচিং" ব্যবহার করুন:

IObject concrete1 = new ObjectImplementation1();
IObject concrete2 = new ObjectImplementation2();

switch (concrete1)
{
    case ObjectImplementation1 c1: return "type 1";         
    case ObjectImplementation2 c2: return "type 2";         
}

0

আমি ব্যবহার করি

    public T Store<T>()
    {
        Type t = typeof(T);

        if (t == typeof(CategoryDataStore))
            return (T)DependencyService.Get<IDataStore<ItemCategory>>();
        else
            return default(T);
    }

0

সঙ্গে কাজ করা উচিত

ঘটণা লিপিবদ্ধকরণ _:

মত:

int i = 1;
bool b = true;
double d = 1.1;
object o = i; // whatever you want

switch (o)
{
    case int _:
        Answer.Content = "You got the int";
        break;
    case double _:
        Answer.Content = "You got the double";
        break;
    case bool _:
        Answer.Content = "You got the bool";
        break;
}

0

আপনি যে শ্রেণীর প্রত্যাশা করছেন তা যদি জানেন তবে এখনও আপনার কাছে কোনও অবজেক্ট নেই তবে আপনি এটি করতে পারেন:

private string GetAcceptButtonText<T>() where T : BaseClass, new()
{
    switch (new T())
    {
        case BaseClassReview _: return "Review";
        case BaseClassValidate _: return "Validate";
        case BaseClassAcknowledge _: return "Acknowledge";
        default: return "Accept";
    }
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.