সি # তে বৈষম্যমূলক ইউনিয়ন


94

[দ্রষ্টব্য: এই প্রশ্নের মূল শিরোনাম " সি # তে ইশ) শৈলী ইউনিয়ন ছিল " তবে জেফের মন্তব্য আমাকে অবহিত করেছিল, স্পষ্টতই এই কাঠামোটিকে 'বৈষম্যমূলক ইউনিয়ন' বলা হয়]

এই প্রশ্নের ভারবসটি ক্ষমা করুন।

আমার কাছে ইতিমধ্যে এসও তে একই ধরণের শোনার প্রশ্ন রয়েছে couple তবে তারা মনে করে ইউনিয়নের মেমরি সাশ্রয়ী সুবিধাগুলিতে বা আন্তঃব্যবহারের জন্য এটি ব্যবহারে মনোনিবেশ করছে। এই জাতীয় প্রশ্নের উদাহরণ এখানে দেওয়া হয়েছে

ইউনিয়ন টাইপ জিনিস রাখার আমার ইচ্ছা কিছুটা আলাদা।

আমি এই মুহুর্তে এমন কিছু কোড লিখছি যা কিছুটা দেখতে দেখতে এমন বস্তু উত্পন্ন করে

public class ValueWrapper
{
    public DateTime ValueCreationDate;
    // ... other meta data about the value

    public object ValueA;
    public object ValueB;
}

খুব জটিল জিনিস আমি মনে করি আপনি সম্মত হবেন। জিনিসটি হ'ল কেবল ValueAকয়েকটি নির্দিষ্ট ধরণের হতে পারে (যাক বলে নেওয়া যাক string, intএবং Foo(যা একটি শ্রেণি) এবংValueB অন্য ধরণের ছোট ছোট সেট হতে পারে these আমি এই মানগুলিকে অবজেক্ট হিসাবে বিবেচনা করতে পছন্দ করি না (আমি উষ্ণ সুন্দর স্মাগলি অনুভূতি চাই কিছুটা সুরক্ষার সাথে কোডিং)।

সুতরাং আমি ভ্যালুএ যুক্তিসঙ্গতভাবে একটি নির্দিষ্ট ধরণের একটি রেফারেন্স এই সত্যটি প্রকাশ করার জন্য একটি তুচ্ছ ছোট্ট র‍্যাপার ক্লাস লেখার বিষয়ে চিন্তা করি। আমি ক্লাসটিকে ডেকেছিলাম Unionকারণ আমি যা অর্জন করতে চাইছি তা আমাকে সি এর ইউনিয়ন ধারণার স্মরণ করিয়ে দিয়েছে what

public class Union<A, B, C>
{
    private readonly Type type; 
    public readonly A a;
    public readonly B b;
    public readonly C c;

    public A A{get {return a;}}
    public B B{get {return b;}}
    public C C{get {return c;}}

    public Union(A a)
    {
        type = typeof(A);
        this.a = a;
    }

    public Union(B b)
    {
        type = typeof(B);
        this.b = b;
    }

    public Union(C c)
    {
        type = typeof(C);
        this.c = c;
    }

    /// <summary>
    /// Returns true if the union contains a value of type T
    /// </summary>
    /// <remarks>The type of T must exactly match the type</remarks>
    public bool Is<T>()
    {
        return typeof(T) == type;
    }

    /// <summary>
    /// Returns the union value cast to the given type.
    /// </summary>
    /// <remarks>If the type of T does not exactly match either X or Y, then the value <c>default(T)</c> is returned.</remarks>
    public T As<T>()
    {
        if(Is<A>())
        {
            return (T)(object)a;    // Is this boxing and unboxing unavoidable if I want the union to hold value types and reference types? 
            //return (T)x;          // This will not compile: Error = "Cannot cast expression of type 'X' to 'T'."
        }

        if(Is<B>())
        {
            return (T)(object)b; 
        }

        if(Is<C>())
        {
            return (T)(object)c; 
        }

        return default(T);
    }
}

এই শ্রেণিটি ব্যবহার করে ভ্যালুওয়্যার্পারটি এখন এর মতো দেখাচ্ছে

public class ValueWrapper2
{
    public DateTime ValueCreationDate;
    public  Union<int, string, Foo> ValueA;
    public  Union<double, Bar, Foo> ValueB;
}

যা আমি অর্জন করতে চেয়েছিলাম তার মতো কিছু তবে আমি একটি মোটামুটি গুরুত্বপূর্ণ উপাদানটি অনুপস্থিত - যা নীচের কোডটি দেখায় যেমন ইস এবং এ্যাস ফাংশনগুলিকে কল করার সময় প্রয়োগকারী প্রকারের পরীক্ষা করা হয় comp

    public void DoSomething()
    {
        if(ValueA.Is<string>())
        {
            var s = ValueA.As<string>();
            // .... do somethng
        }

        if(ValueA.Is<char>()) // I would really like this to be a compile error
        {
            char c = ValueA.As<char>();
        }
    }

আইএমও ভ্যালুএকে জিজ্ঞাসা করা বৈধ নয় charযেহেতু এটির একটি সংজ্ঞা থেকে এটি পরিষ্কার নয় যে এটি একটি নয় - এটি একটি প্রোগ্রামিং ত্রুটি এবং আমি সংকলকটি এটি গ্রহণ করতে চাই। [এছাড়াও যদি আমি এটি সঠিকভাবে পেতে পারি তবে (আশা করি) আমিও বুদ্ধিমান হব - যা একটি উত্সাহ হবে]

এটি অর্জনের জন্য আমি সংকলককে বলতে চাই যে টাইপটি Tএ, বি বা সি এর মধ্যে একটি হতে পারে

    public bool Is<T>() where T : A 
                           or T : B // Yes I know this is not legal!
                           or T : C 
    {
        return typeof(T) == type;
    } 

আমি যা অর্জন করতে চাই তা কি সম্ভব হলে কারও কি ধারণা আছে? বা এই ক্লাসটি প্রথম স্থানে লেখার জন্য আমি কি সরল বোকা?

আগাম ধন্যবাদ.


4
সি ইউনিয়ন ব্যবহার মান ধরনের জন্য C # এর বাস্তবায়িত করা যাবে StructLayout(LayoutKind.Explicit)এবং FieldOffset। এটি অবশ্যই রেফারেন্সের ধরণের মাধ্যমে করা যায় না। আপনি যা করছেন তা কোনও সি ইউনিয়নের মতো নয়।
ব্রায়ান

4
এটিকে প্রায়শই একটি বৈষম্যমূলক ইউনিয়ন বলা হয় ।
জেফ হার্ডি

থ্যাঙ্কস জেফ - আমি এই পদটি সম্পর্কে অসচেতন ছিলাম তবে এটি আমি যা অর্জন করতে চাই তা খুব স্পষ্টভাবে
ক্রিস ফেট্রেল

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

4
বৈষম্যমূলক ইউনিয়নের আর একটি নাম একটি যোগফল।
cdiggins

উত্তর:


116

উপরে প্রদত্ত টাইপ-চেকিং এবং টাইপ-কাস্টিং সমাধানগুলি আমি সত্যিই পছন্দ করি না, সুতরাং এখানে 100% টাইপ-নিরাপদ ইউনিয়ন যা আপনি ভুল ডাটাটাইপ ব্যবহার করার চেষ্টা করলে সংকলন ত্রুটি ছুঁড়ে ফেলবে:

using System;

namespace Juliet
{
    class Program
    {
        static void Main(string[] args)
        {
            Union3<int, char, string>[] unions = new Union3<int,char,string>[]
                {
                    new Union3<int, char, string>.Case1(5),
                    new Union3<int, char, string>.Case2('x'),
                    new Union3<int, char, string>.Case3("Juliet")
                };

            foreach (Union3<int, char, string> union in unions)
            {
                string value = union.Match(
                    num => num.ToString(),
                    character => new string(new char[] { character }),
                    word => word);
                Console.WriteLine("Matched union with value '{0}'", value);
            }

            Console.ReadLine();
        }
    }

    public abstract class Union3<A, B, C>
    {
        public abstract T Match<T>(Func<A, T> f, Func<B, T> g, Func<C, T> h);
        // private ctor ensures no external classes can inherit
        private Union3() { } 

        public sealed class Case1 : Union3<A, B, C>
        {
            public readonly A Item;
            public Case1(A item) : base() { this.Item = item; }
            public override T Match<T>(Func<A, T> f, Func<B, T> g, Func<C, T> h)
            {
                return f(Item);
            }
        }

        public sealed class Case2 : Union3<A, B, C>
        {
            public readonly B Item;
            public Case2(B item) { this.Item = item; }
            public override T Match<T>(Func<A, T> f, Func<B, T> g, Func<C, T> h)
            {
                return g(Item);
            }
        }

        public sealed class Case3 : Union3<A, B, C>
        {
            public readonly C Item;
            public Case3(C item) { this.Item = item; }
            public override T Match<T>(Func<A, T> f, Func<B, T> g, Func<C, T> h)
            {
                return h(Item);
            }
        }
    }
}

4
হ্যাঁ, আপনি যদি টাইপসেফ বৈষম্যমূলক ইউনিয়নগুলি চান, আপনার প্রয়োজন হবে matchএবং এটি যে কোনও হিসাবে পাওয়ার জন্য এটি উত্তম উপায়।
পাভেল মিনায়েভ

21
এবং যদি সেই সমস্ত বয়লারপ্লেট কোডটি আপনাকে নামিয়ে দেয় তবে আপনি এই বাস্তবায়নটি চেষ্টা করতে পারেন যা এর পরিবর্তে কেসগুলিকে স্পষ্টভাবে ট্যাগ করে: পেস্টবিন . com / এইডিভিভিএইচ 2 আর । ঘটনাচক্রে এই স্টাইলটি অভ্যন্তরীণভাবে ইউনিয়নগুলির প্রতিনিধিত্ব করার জন্য এফ # এবং ওসিএএমএল এর সাথে খুব মিল।
জুলিয়েট

4
আমি জুলিয়েটের সংক্ষিপ্ত কোডটি পছন্দ করি তবে প্রকারগুলি <int, int, স্ট্রিং> হয় তবে কী হবে? দ্বিতীয় কনস্ট্রাক্টরকে আপনি কীভাবে কল করবেন?
রবার্ট জেপ্পেসেন

4
আমি জানি না এটির কীভাবে 100 টি উপাখড়ি নেই। এটা সৌন্দর্যের জিনিস!
পাওলো ফালেবেলা

6
@ এনেক্সাস এফ # তে এই ধরণের বিবেচনা করুন:type Result = Success of int | Error of int
অ্যালেক্সফক্সগিল

33

আমি স্বীকৃত সমাধানের দিকটি পছন্দ করি তবে এটি তিনটির বেশি আইটেমের ইউনিয়নগুলির জন্য ভাল স্কেল করে না (উদাহরণস্বরূপ 9 আইটেমের ইউনিয়ন 9 শ্রেণির সংজ্ঞা প্রয়োজন)।

এখানে আরও একটি পদ্ধতি যা সংকলন সময়ে 100% টাইপ-নিরাপদ, তবে এটি বড় ইউনিয়নগুলিতে বৃদ্ধি করা সহজ।

public class UnionBase<A>
{
    dynamic value;

    public UnionBase(A a) { value = a; } 
    protected UnionBase(object x) { value = x; }

    protected T InternalMatch<T>(params Delegate[] ds)
    {
        var vt = value.GetType();    
        foreach (var d in ds)
        {
            var mi = d.Method;

            // These are always true if InternalMatch is used correctly.
            Debug.Assert(mi.GetParameters().Length == 1);
            Debug.Assert(typeof(T).IsAssignableFrom(mi.ReturnType));

            var pt = mi.GetParameters()[0].ParameterType;
            if (pt.IsAssignableFrom(vt))
                return (T)mi.Invoke(null, new object[] { value });
        }
        throw new Exception("No appropriate matching function was provided");
    }

    public T Match<T>(Func<A, T> fa) { return InternalMatch<T>(fa); }
}

public class Union<A, B> : UnionBase<A>
{
    public Union(A a) : base(a) { }
    public Union(B b) : base(b) { }
    protected Union(object x) : base(x) { }
    public T Match<T>(Func<A, T> fa, Func<B, T> fb) { return InternalMatch<T>(fa, fb); }
}

public class Union<A, B, C> : Union<A, B>
{
    public Union(A a) : base(a) { }
    public Union(B b) : base(b) { }
    public Union(C c) : base(c) { }
    protected Union(object x) : base(x) { }
    public T Match<T>(Func<A, T> fa, Func<B, T> fb, Func<C, T> fc) { return InternalMatch<T>(fa, fb, fc); }
}

public class Union<A, B, C, D> : Union<A, B, C>
{
    public Union(A a) : base(a) { }
    public Union(B b) : base(b) { }
    public Union(C c) : base(c) { }
    public Union(D d) : base(d) { }
    protected Union(object x) : base(x) { }
    public T Match<T>(Func<A, T> fa, Func<B, T> fb, Func<C, T> fc, Func<D, T> fd) { return InternalMatch<T>(fa, fb, fc, fd); }
}

public class Union<A, B, C, D, E> : Union<A, B, C, D>
{
    public Union(A a) : base(a) { }
    public Union(B b) : base(b) { }
    public Union(C c) : base(c) { }
    public Union(D d) : base(d) { }
    public Union(E e) : base(e) { }
    protected Union(object x) : base(x) { }
    public T Match<T>(Func<A, T> fa, Func<B, T> fb, Func<C, T> fc, Func<D, T> fd, Func<E, T> fe) { return InternalMatch<T>(fa, fb, fc, fd, fe); }
}

public class DiscriminatedUnionTest : IExample
{
    public Union<int, bool, string, int[]> MakeUnion(int n)
    {
        return new Union<int, bool, string, int[]>(n);
    }

    public Union<int, bool, string, int[]> MakeUnion(bool b)
    {
        return new Union<int, bool, string, int[]>(b);
    }

    public Union<int, bool, string, int[]> MakeUnion(string s)
    {
        return new Union<int, bool, string, int[]>(s);
    }

    public Union<int, bool, string, int[]> MakeUnion(params int[] xs)
    {
        return new Union<int, bool, string, int[]>(xs);
    }

    public void Print(Union<int, bool, string, int[]> union)
    {
        var text = union.Match(
            n => "This is an int " + n.ToString(),
            b => "This is a boolean " + b.ToString(),
            s => "This is a string" + s,
            xs => "This is an array of ints " + String.Join(", ", xs));
        Console.WriteLine(text);
    }

    public void Run()
    {
        Print(MakeUnion(1));
        Print(MakeUnion(true));
        Print(MakeUnion("forty-two"));
        Print(MakeUnion(0, 1, 1, 2, 3, 5, 8));
    }
}

+1 এটিকে আরও অনুমোদন দেওয়া উচিত; সমস্ত ধরণের ইউনিয়নের অনুমতি দেওয়ার জন্য আপনি যেভাবে এটিকে যথেষ্ট নমনীয় করেছেন তা আমার পছন্দ।
পল ডি'অস্ট

আপনার সমাধানের নমনীয়তা এবং সংকোচনের জন্য +1। যদিও কিছু বিবরণ আমাকে বিরক্ত করছে, যদিও। আমি প্রত্যেককে পৃথক মন্তব্য হিসাবে পোস্ট করব:
স্টাকেক্স - 16

4
১. প্রতিচ্ছবি ব্যবহারের কারণে কিছু পরিস্থিতিতে খুব বেশি পারফরম্যান্সের জরিমানা হতে পারে, যেহেতু বৈষম্যমূলক ইউনিয়নগুলি তাদের মৌলিক প্রকৃতির কারণে খুব সম্ভবত ব্যবহৃত হতে পারে।
স্টাকেক্স

4
dynamic & জেনেরিকের ব্যবহার UnionBase<A>এবং উত্তরাধিকার শৃঙ্খলা অপ্রয়োজনীয় বলে মনে হচ্ছে। UnionBase<A>অ-জেনেরিক তৈরি করুন , কনস্ট্রাক্টরটি নিচ্ছেন Aএবং valueএকটি তৈরি করুন object(এটি এটি যাই হোক না কেন; এটি ঘোষণায় কোনও বাড়তি সুবিধা নেই dynamic)। তারপরে প্রতিটি Union<…>ক্লাস থেকে সরাসরি প্রাপ্ত UnionBase। এটির সুবিধাটি রয়েছে যে কেবল সঠিক Match<T>(…)পদ্ধতিটিই প্রকাশিত হবে। (যেমনটি এখন, উদাহরণস্বরূপ Union<A, B>একটি Match<T>(Func<A, T> fa)A
ওভারলোডকে বহিঃপ্রকাশ করে যা বদ্ধমূল্যটি

4
আপনি আমার লাইব্রেরিটি ওয়ানওফকে দরকারী মনে করতে পারেন এটি কমবেশি এটি করে তবে নুগেটে রয়েছে :) github.com/mcintyre321/OneOf
mcintyre321

20

আমি এই বিষয়ে কিছু ব্লগ পোস্ট লিখেছি যা কার্যকর হতে পারে:

ধরা যাক আপনার শপিং কার্টের দৃশ্যটি তিনটি রাষ্ট্রের সাথে রয়েছে: "খালি", "অ্যাক্টিভ" এবং "অর্থ প্রদান করা" প্রত্যেকে আলাদা আলাদা আচরণ করে।

  • আপনার তৈরির একটি ICartStateইন্টারফেস রয়েছে যা সমস্ত রাজ্যের সমান হয় (এবং এটি কেবল একটি খালি মার্কার ইন্টারফেস হতে পারে)
  • আপনি তিনটি শ্রেণি তৈরি করেন যা সেই ইন্টারফেস প্রয়োগ করে। (ক্লাসগুলির উত্তরাধিকারের সম্পর্কের মধ্যে থাকতে হবে না)
  • ইন্টারফেসটিতে একটি "ভাঁজ" পদ্ধতি রয়েছে, যার মাধ্যমে আপনি প্রতিটি রাজ্য বা কেস যা আপনাকে পরিচালনা করতে হবে তার জন্য একটি ল্যাম্বদা পাস করে।

আপনি সি # থেকে এফ # রানটাইমটি ব্যবহার করতে পারেন তবে হালকা ওজনের বিকল্প হিসাবে, কোডটি তৈরি করার জন্য আমি একটি টি টি 4 টেম্পলেট লিখেছি।

ইন্টারফেসটি এখানে:

partial interface ICartState
{
  ICartState Transition(
        Func<CartStateEmpty, ICartState> cartStateEmpty,
        Func<CartStateActive, ICartState> cartStateActive,
        Func<CartStatePaid, ICartState> cartStatePaid
        );
}

এবং বাস্তবায়ন এখানে:

class CartStateEmpty : ICartState
{
  ICartState ICartState.Transition(
        Func<CartStateEmpty, ICartState> cartStateEmpty,
        Func<CartStateActive, ICartState> cartStateActive,
        Func<CartStatePaid, ICartState> cartStatePaid
        )
  {
        // I'm the empty state, so invoke cartStateEmpty 
      return cartStateEmpty(this);
  }
}

class CartStateActive : ICartState
{
  ICartState ICartState.Transition(
        Func<CartStateEmpty, ICartState> cartStateEmpty,
        Func<CartStateActive, ICartState> cartStateActive,
        Func<CartStatePaid, ICartState> cartStatePaid
        )
  {
        // I'm the active state, so invoke cartStateActive
      return cartStateActive(this);
  }
}

class CartStatePaid : ICartState
{
  ICartState ICartState.Transition(
        Func<CartStateEmpty, ICartState> cartStateEmpty,
        Func<CartStateActive, ICartState> cartStateActive,
        Func<CartStatePaid, ICartState> cartStatePaid
        )
  {
        // I'm the paid state, so invoke cartStatePaid
      return cartStatePaid(this);
  }
}

এখন আপনি প্রসারিত বলা যাক CartStateEmptyএবং CartStateActiveএকটি সঙ্গে AddItemপদ্ধতি যা হয় না দ্বারা বাস্তবায়িতCartStatePaid

এবং এছাড়াও এর CartStateActiveএকটি আছে বলে দিনPay পদ্ধতি রয়েছে যা অন্যান্য রাজ্যগুলির কাছে নেই।

তারপরে এখানে এমন কিছু কোড রয়েছে যা এটি ব্যবহারে দেখায় - দুটি আইটেম যুক্ত করে এবং পরে কার্টের জন্য অর্থ প্রদান করে:

public ICartState AddProduct(ICartState currentState, Product product)
{
    return currentState.Transition(
        cartStateEmpty => cartStateEmpty.AddItem(product),
        cartStateActive => cartStateActive.AddItem(product),
        cartStatePaid => cartStatePaid // not allowed in this case
        );

}

public void Example()
{
    var currentState = new CartStateEmpty() as ICartState;

    //add some products 
    currentState = AddProduct(currentState, Product.ProductX);
    currentState = AddProduct(currentState, Product.ProductY);

    //pay 
    const decimal paidAmount = 12.34m;
    currentState = currentState.Transition(
        cartStateEmpty => cartStateEmpty,  // not allowed in this case
        cartStateActive => cartStateActive.Pay(paidAmount),
        cartStatePaid => cartStatePaid     // not allowed in this case
        );
}    

মনে রাখবেন যে এই কোডটি পুরোপুরি টাইপসেফ - কোনও জায়গায় castালাই বা শর্ত নেই এবং আপনি যদি খালি কার্টের জন্য অর্থ প্রদানের চেষ্টা করেন তবে সংকলক ত্রুটিগুলি বলুন।


আকর্ষণীয় ব্যবহারের ক্ষেত্রে। আমার জন্য, বস্তুগুলিতে বৈষম্যমূলক ইউনিয়নগুলি প্রয়োগ করা তারা নিজেরাই বেশ ভার্জোজ হয়। আপনার মডেলের উপর ভিত্তি করে স্যুইচ এক্সপ্রেশনগুলি ব্যবহার করে এমন একটি কার্যক্ষম-শৈলীর বিকল্প: gist.github.com/dcuccia/4029f1cddd7914dc1ae676d8c4af7866 । আপনি দেখতে পাচ্ছেন যে কেবলমাত্র একটি "সুখী" পথ থাকলে ডিইউগুলি সত্যই প্রয়োজনীয় নয়, তবে ব্যবসায়ের যুক্তির নিয়মের উপর নির্ভর করে কোনও পদ্ধতি যখন এক প্রকার বা অন্য কোনও দেশে ফিরে আসতে পারে তখন তারা খুব সহায়ক হয়।
ডেভিড কচিয়া

14

এটি করার জন্য আমি একটি লাইব্রেরি লিখেছি https://github.com/mcintyre321/OneOf এ

ওয়ান-প্যাকেজ ইনস্টল করুন

Dাবি করার জন্য এটিতে জেনেরিক ধরণের রয়েছে যেমন OneOf<T0, T1>সমস্ত উপায় OneOf<T0, ..., T9>। এর প্রত্যেকটির একটিতে .Matchএবং একটি .Switchবিবৃতি রয়েছে যা আপনি সংকলক নিরাপদ টাইপড আচরণের জন্য ব্যবহার করতে পারেন, যেমন:

`` `

OneOf<string, ColorName, Color> backgroundColor = getBackground(); 
Color c = backgroundColor.Match(
    str => CssHelper.GetColorFromString(str),
    name => new Color(name),
    col => col
);

`` `


7

আমি নিশ্চিত না যে আমি আপনার লক্ষ্যটি পুরোপুরি বুঝতে পেরেছি। সি-তে, একটি ইউনিয়ন এমন একটি কাঠামো যা একাধিক ক্ষেত্রের জন্য একই মেমরি অবস্থান ব্যবহার করে। উদাহরণ স্বরূপ:

typedef union
{
    float real;
    int scalar;
} floatOrScalar;

floatOrScalarইউনিয়ন একটি float, বা কোন int হিসাবে ব্যবহার করা যেতে পারে, কিন্তু তারা উভয় একই মেমরি স্পেস ব্যবহার করা হবে। একটি পরিবর্তন অন্য পরিবর্তন। আপনি সি # তে স্ট্রাক্ট দিয়ে একই জিনিস অর্জন করতে পারেন:

[StructLayout(LayoutKind.Explicit)]
struct FloatOrScalar
{
    [FieldOffset(0)]
    public float Real;
    [FieldOffset(0)]
    public int Scalar;
}

উপরের কাঠামোটিতে 64 বিটের পরিবর্তে মোট 32 বিট ব্যবহার করা হয়েছে। এটি কেবল একটি কাঠামো দিয়েই সম্ভব। উপরে আপনার উদাহরণটি একটি শ্রেণি, এবং সিএলআর প্রকৃতি দেওয়া মেমরির দক্ষতার বিষয়ে কোনও গ্যারান্টি দেয় না। আপনি যদি Union<A, B, C>এক ধরণের থেকে অন্য প্রকারে পরিবর্তন করেন তবে অগত্যা আপনি মেমরিটি পুনরায় ব্যবহার করবেন না ... সম্ভবত, আপনি একটি নতুন ধরণের গাদা উপর বরাদ্দ করছেন এবং ব্যাকিং objectফিল্ডে একটি আলাদা পয়েন্টার বাদ দিচ্ছেন । প্রকৃত ইউনিয়নের বিপরীতে , আপনার পদ্ধতির কারণে আপনি যদি আপনার ইউনিয়নের ধরণটি ব্যবহার না করেন তবে আপনি যে পরিমাণ অন্যথায় পেতে পারেন তার চেয়ে আসলে আরও বেশি আঘাতের কারণ হতে পারে।


যেমনটি আমি আমার প্রশ্নে উল্লেখ করেছি, আমার অনুপ্রেরণার চেয়ে ভাল স্মৃতি দক্ষতা ছিল না। আমার লক্ষ্য কী তা আরও ভালভাবে প্রতিবিম্বিত করতে আমি প্রশ্নের শিরোনামটি পরিবর্তন করেছি - "সি (ইশ) ইউনিয়নের মূল শিরোনাম
হ'ল দূরদর্শিতা

আপনি যা করার চেষ্টা করছেন তার জন্য একটি বৈষম্যমূলক ইউনিয়ন পুরোপুরি অনেক বেশি অর্থবোধ করে। এটি কম্পাইল-টাইম চেক করা হিসাবে ... আমি। নেট 4 এবং কোড চুক্তিগুলি সন্ধান করব। কোড চুক্তিগুলির সাথে, একটি সংকলন-সময় চুক্তি কার্যকর করা সম্ভব হতে পারে e প্রয়োজনীয়তা যা আপনার প্রয়োজনীয়তাগুলি .Is <T> অপারেটরে প্রয়োগ করে।
জ্রিস্টা

আমার ধারণা আমি এখনও সাধারণ অনুশীলনে ইউনিয়ন ব্যবহার সম্পর্কে প্রশ্ন করতে হবে। এমনকি সি / সি ++ তেও ইউনিয়নগুলি ঝুঁকিপূর্ণ জিনিস এবং চূড়ান্ত যত্ন সহকারে অবশ্যই এটি ব্যবহার করা উচিত। আমি কৌতূহলী যে আপনি কেন এই জাতীয় নির্মাণের প্রয়োজন সি # তে আনতে হবে ... আপনি এর থেকে কী মূল্য পেয়েছেন?
জ্রিস্টা

2
char foo = 'B';

bool bar = foo is int;

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


2

আপনি যদি একাধিক ধরণের অনুমতি দেন তবে আপনি ধরণের সুরক্ষা অর্জন করতে পারবেন না (যদি না ধরণেরগুলি সম্পর্কিত হয়)।

আপনি কোনও প্রকারের সুরক্ষা অর্জন করতে পারবেন না এবং অর্জন করতে পারবেন না, আপনি কেবল ফিল্ডঅফसेट ব্যবহার করে বাইট-মান-সুরক্ষা অর্জন করতে পারবেন।

এটা অনেক আরো জানার জন্য চাই একটি জেনেরিক আছে ValueWrapper<T1, T2>সঙ্গে T1 ValueAএবং T2 ValueB...

PS: টাইপ-সুরক্ষার কথা বলার অর্থ আমি সংকলন-টাইপ টাইপ-সুরক্ষা।

আপনার যদি কোনও কোড র‌্যাপারের প্রয়োজন হয় (পরিবর্তনের উপর ব্যবসায় যুক্তি সম্পাদন করে আপনি এর লাইন ধরে কিছু ব্যবহার করতে পারেন:

public class Wrapper
{
    public ValueHolder<int> v1 = 5;
    public ValueHolder<byte> v2 = 8;
}

public struct ValueHolder<T>
    where T : struct
{
    private T value;

    public ValueHolder(T value) { this.value = value; }

    public static implicit operator T(ValueHolder<T> valueHolder) { return valueHolder.value; }
    public static implicit operator ValueHolder<T>(T value) { return new ValueHolder<T>(value); }
}

সহজে ব্যবহারের জন্য আপনি ব্যবহার করতে পারেন (এতে পারফরম্যান্সের সমস্যা রয়েছে তবে এটি খুব সহজ):

public class Wrapper
{
    private object v1;
    private object v2;

    public T GetValue1<T>() { if (v1.GetType() != typeof(T)) throw new InvalidCastException(); return (T)v1; }
    public void SetValue1<T>(T value) { v1 = value; }

    public T GetValue2<T>() { if (v2.GetType() != typeof(T)) throw new InvalidCastException(); return (T)v2; }
    public void SetValue2<T>(T value) { v2 = value; }
}

//usage:
Wrapper wrapper = new Wrapper();
wrapper.SetValue1("aaaa");
wrapper.SetValue2(456);

string s = wrapper.GetValue1<string>();
DateTime dt = wrapper.GetValue1<DateTime>();//InvalidCastException

আপনার ভ্যালুওয়্যার্পারকে জেনেরিক করার পরামর্শটি সুস্পষ্ট উত্তরের মতো মনে হচ্ছে তবে এটি আমি যা করছি তাতে সমস্যা সৃষ্টি করে। মূলত, আমার কোড কিছু পাঠ্য লাইনের বিশ্লেষণ করে এই মোড়কযুক্ত বস্তু তৈরি করছে। সুতরাং আমার কাছে ভ্যালুওয়্যার্পার মেকভালিউর্যাপার (স্ট্রিং টেক্সট) মতো একটি পদ্ধতি রয়েছে। যদি আমি মোড়কে জেনেরিক করি তবে আমাকে জেনেরিক হওয়ার জন্য মেকভ্যালুওয়্যার্পারের স্বাক্ষরটি পরিবর্তন করতে হবে এবং তারপরে এর অর্থ দাঁড়ায় যে কলিং কোডটি কী ধরণের প্রত্যাশা করা উচিত তা জানা দরকার এবং আমি পাঠ্যটি বিশ্লেষণ করার আগে এটিকে আগাম জানি না don't ...
ক্রিস ফেট্রেল

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

আমার দেওয়া কোডটি কেবলমাত্র যুক্তিযুক্ত যুক্তির জন্য বলে মনে করা হচ্ছে। আপনার পদ্ধতির সাথে সমস্যাটি হ'ল সংকলন সময়ে ইউনিয়নে কোন মান সংরক্ষণ করা হয় তা আপনি কখনই জানেন না। এর অর্থ আপনি যখনই ইউনিয়ন অবজেক্টটিতে অ্যাক্সেস করেন তখনই আপনাকে যদি বিবৃতিগুলি পরিবর্তন করতে হয় বা ব্যবহার করতে হবে, কারণ এই বস্তুগুলি কোনও সাধারণ কার্যকারিতা ভাগ করে না দেয়! আপনি কীভাবে আপনার কোডটিতে মোড়কের জিনিসগুলি ব্যবহার করতে যাচ্ছেন? এছাড়াও আপনি রানটাইমে জেনেরিক অবজেক্টগুলি তৈরি করতে পারেন (ধীর, তবে সম্ভব)। এর সাথে আরও একটি সহজ বিকল্পটি আমার সম্পাদিত পোস্টে রয়েছে।
জারোস্লাভ জান্দেক

আপনার কোডটিতে এখনই মূলত কোনও অর্থবহ সংকলন-টাইম চেক নেই - আপনি গতিশীল অবজেক্টগুলিও চেষ্টা করতে পারেন (রানটাইমের সময় ডায়নামিক টাইপ চেকিং)।
জারোস্লাভ জান্দেক

2

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

class Union {
    public interface AllowedType<T> { };

    internal object val;

    internal System.Type type;
}

static class UnionEx {
    public static T As<U,T>(this U x) where U : Union, Union.AllowedType<T> {
        return x.type == typeof(T) ?(T)x.val : default(T);
    }

    public static void Set<U,T>(this U x, T newval) where U : Union, Union.AllowedType<T> {
        x.val = newval;
        x.type = typeof(T);
    }

    public static bool Is<U,T>(this U x) where U : Union, Union.AllowedType<T> {
        return x.type == typeof(T);
    }
}

class MyType : Union, Union.AllowedType<int>, Union.AllowedType<string> {}

class TestIt
{
    static void Main()
    {
        MyType bla = new MyType();
        bla.Set(234);
        System.Console.WriteLine(bla.As<MyType,int>());
        System.Console.WriteLine(bla.Is<MyType,string>());
        System.Console.WriteLine(bla.Is<MyType,int>());

        bla.Set("test");
        System.Console.WriteLine(bla.As<MyType,string>());
        System.Console.WriteLine(bla.Is<MyType,string>());
        System.Console.WriteLine(bla.Is<MyType,int>());

        // compile time errors!
        // bla.Set('a'); 
        // bla.Is<MyType,char>()
    }
}

এটি কিছু চমত্কার আপ ব্যবহার করতে পারে। বিশেষত, আমি কীভাবে / ইস / সেট-তে টাইপ প্যারামিটারগুলি থেকে মুক্তি পেতে পারি তা বুঝতে পারি না (এক ধরণের প্যারামিটার নির্দিষ্ট করার কোনও উপায় নেই এবং সি # কে অন্য একটি চিত্র দেওয়া যাক?)


2

সুতরাং আমি এই একই সমস্যাটি বহুবার আঘাত করেছি এবং আমি সবেমাত্র একটি সমাধান নিয়ে এসেছি যা আমার সিনট্যাক্সটি পেতে চায় (ইউনিয়নের ধরণের বাস্তবায়নে কিছু কদর্য ব্যয়ের পরে।)

পুনরুদ্ধার করতে: আমরা কল সাইটে এই ধরণের ব্যবহার করতে চাই।

Union<int, string> u;

u = 1492;
int yearColumbusDiscoveredAmerica = u;

u = "hello world";
string traditionalGreeting = u;

var answers = new SortedList<string, Union<int, string, DateTime>>();
answers["life, the universe, and everything"] = 42;
answers["D-Day"] = new DateTime(1944, 6, 6);
answers["C#"] = "is awesome";

আমরা নিম্নলিখিত উদাহরণগুলি সংকলন করতে ব্যর্থ করতে চাই, তবে, যাতে আমরা টাইপ সুরক্ষা পেতে পারি।

DateTime dateTimeColumbusDiscoveredAmerica = u;
Foo fooInstance = u;

অতিরিক্ত creditণের জন্য, আসুন একেবারে প্রয়োজনের চেয়ে বেশি জায়গা না নেওয়া।

যা যা বলা হয়েছে তার সাথে, এখানে দুটি জেনেরিক ধরণের পরামিতিগুলির জন্য আমার বাস্তবায়ন। তিন, চার এবং আরও বিভিন্ন ধরণের পরামিতিগুলির জন্য বাস্তবায়নটি সরাসরি-এগিয়ে forward

public abstract class Union<T1, T2>
{
    public abstract int TypeSlot
    {
        get;
    }

    public virtual T1 AsT1()
    {
        throw new TypeAccessException(string.Format(
            "Cannot treat this instance as a {0} instance.", typeof(T1).Name));
    }

    public virtual T2 AsT2()
    {
        throw new TypeAccessException(string.Format(
            "Cannot treat this instance as a {0} instance.", typeof(T2).Name));
    }

    public static implicit operator Union<T1, T2>(T1 data)
    {
        return new FromT1(data);
    }

    public static implicit operator Union<T1, T2>(T2 data)
    {
        return new FromT2(data);
    }

    public static implicit operator Union<T1, T2>(Tuple<T1, T2> data)
    {
        return new FromTuple(data);
    }

    public static implicit operator T1(Union<T1, T2> source)
    {
        return source.AsT1();
    }

    public static implicit operator T2(Union<T1, T2> source)
    {
        return source.AsT2();
    }

    private class FromT1 : Union<T1, T2>
    {
        private readonly T1 data;

        public FromT1(T1 data)
        {
            this.data = data;
        }

        public override int TypeSlot 
        { 
            get { return 1; } 
        }

        public override T1 AsT1()
        { 
            return this.data;
        }

        public override string ToString()
        {
            return this.data.ToString();
        }

        public override int GetHashCode()
        {
            return this.data.GetHashCode();
        }
    }

    private class FromT2 : Union<T1, T2>
    {
        private readonly T2 data;

        public FromT2(T2 data)
        {
            this.data = data;
        }

        public override int TypeSlot 
        { 
            get { return 2; } 
        }

        public override T2 AsT2()
        { 
            return this.data;
        }

        public override string ToString()
        {
            return this.data.ToString();
        }

        public override int GetHashCode()
        {
            return this.data.GetHashCode();
        }
    }

    private class FromTuple : Union<T1, T2>
    {
        private readonly Tuple<T1, T2> data;

        public FromTuple(Tuple<T1, T2> data)
        {
            this.data = data;
        }

        public override int TypeSlot 
        { 
            get { return 0; } 
        }

        public override T1 AsT1()
        { 
            return this.data.Item1;
        }

        public override T2 AsT2()
        { 
            return this.data.Item2;
        }

        public override string ToString()
        {
            return this.data.ToString();
        }

        public override int GetHashCode()
        {
            return this.data.GetHashCode();
        }
    }
}

2

এবং ইউনিয়ন / উভয় প্রকারের নেস্টিং ব্যবহার করে ন্যূনতম হলেও এক্সটেনসিবল সমাধানের জন্য আমার প্রচেষ্টা । এছাড়াও মিল পদ্ধতিতে ডিফল্ট পরামিতিগুলির ব্যবহার প্রাকৃতিকভাবে "হয় এক্স বা ডিফল্ট" দৃশ্যে সক্ষম করে।

using System;
using System.Reflection;
using NUnit.Framework;

namespace Playground
{
    [TestFixture]
    public class EitherTests
    {
        [Test]
        public void Test_Either_of_Property_or_FieldInfo()
        {
            var some = new Some(false);
            var field = some.GetType().GetField("X");
            var property = some.GetType().GetProperty("Y");
            Assert.NotNull(field);
            Assert.NotNull(property);

            var info = Either<PropertyInfo, FieldInfo>.Of(field);
            var infoType = info.Match(p => p.PropertyType, f => f.FieldType);

            Assert.That(infoType, Is.EqualTo(typeof(bool)));
        }

        [Test]
        public void Either_of_three_cases_using_nesting()
        {
            var some = new Some(false);
            var field = some.GetType().GetField("X");
            var parameter = some.GetType().GetConstructors()[0].GetParameters()[0];
            Assert.NotNull(field);
            Assert.NotNull(parameter);

            var info = Either<ParameterInfo, Either<PropertyInfo, FieldInfo>>.Of(parameter);
            var name = info.Match(_ => _.Name, _ => _.Name, _ => _.Name);

            Assert.That(name, Is.EqualTo("a"));
        }

        public class Some
        {
            public bool X;
            public string Y { get; set; }

            public Some(bool a)
            {
                X = a;
            }
        }
    }

    public static class Either
    {
        public static T Match<A, B, C, T>(
            this Either<A, Either<B, C>> source,
            Func<A, T> a = null, Func<B, T> b = null, Func<C, T> c = null)
        {
            return source.Match(a, bc => bc.Match(b, c));
        }
    }

    public abstract class Either<A, B>
    {
        public static Either<A, B> Of(A a)
        {
            return new CaseA(a);
        }

        public static Either<A, B> Of(B b)
        {
            return new CaseB(b);
        }

        public abstract T Match<T>(Func<A, T> a = null, Func<B, T> b = null);

        private sealed class CaseA : Either<A, B>
        {
            private readonly A _item;
            public CaseA(A item) { _item = item; }

            public override T Match<T>(Func<A, T> a = null, Func<B, T> b = null)
            {
                return a == null ? default(T) : a(_item);
            }
        }

        private sealed class CaseB : Either<A, B>
        {
            private readonly B _item;
            public CaseB(B item) { _item = item; }

            public override T Match<T>(Func<A, T> a = null, Func<B, T> b = null)
            {
                return b == null ? default(T) : b(_item);
            }
        }
    }
}

1

একবার যদি ভেরিয়েবল অ্যাক্সেস করার চেষ্টা করা হয় যা আরম্ভ করা হয়নি, আপনি ব্যতিক্রম ছুঁড়ে ফেলতে পারেন, অর্থাত্ যদি এটি একটি প্যারামিটার দিয়ে তৈরি করা হয় এবং পরে বি বা সি অ্যাক্সেস করার চেষ্টা করা হয়, তবে এটি বলতে পারে, অসমর্থিত অপারেশন এক্সেক্সশনটি ফেলে দিতে পারে। এটি কাজ করার জন্য আপনার একজন গিটার দরকার।


হ্যাঁ - আমি যে প্রথম সংস্করণটি লিখেছিলাম তা As পদ্ধতিতে ব্যতিক্রম বাড়িয়েছে - তবে এই কোডটিতে অবশ্যই সমস্যাটি তুলে ধরেছে, রানটাইমের চেয়ে সংকলনের সময় আমি এই সম্পর্কে বেশি বলা উচিত।
ক্রিস Fewtrell

1

সি # ল্যাঙ্গুয়েজ ডিজাইন টিম জানুয়ারী 2017 তে বৈষম্যমূলক ইউনিয়নগুলি নিয়ে আলোচনা করেছে https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-01-10.md#discriminated-unions-via-closed-tyype

আপনি https://github.com/dotnet/csharplang/issues/113 এ বৈশিষ্ট্যটির অনুরোধের পক্ষে ভোট দিতে পারেন


0

আপনি আমার সিসা লাইব্রেরিতে যেহেতু টাইপ করুন এর জন্য সিউডো-প্যাটার্ন মেলানো ফাংশনটি রফতানি করতে পারেন । বর্তমানে রানটাইম ওভারহেড রয়েছে, তবে আমি অবশেষে সমস্ত প্রতিনিধিদের সত্যিকারের ক্ষেত্রে বিবৃতি দেওয়ার জন্য একটি সিআইএল বিশ্লেষণ যুক্ত করার পরিকল্পনা করছি।


0

আপনি যে সিনট্যাক্সটি ব্যবহার করেছেন ঠিক তেমনটি করা সম্ভব নয় তবে আরও কিছু ভারবোসিটি এবং অনুলিপি / পেস্ট করে ওভারলোডের রেজোলিউশনটি আপনার পক্ষে কাজটি করা সহজ করে:


// this code is ok
var u = new Union("");
if (u.Value(Is.OfType()))
{
    u.Value(Get.ForType());
}

// and this one will not compile
if (u.Value(Is.OfType()))
{
    u.Value(Get.ForType());
}

এখনই এটি কার্যকরভাবে বাস্তবায়ন করা উচিত:


    public class Union
    {
        private readonly Type type;
        public readonly A a;
        public readonly B b;
        public readonly C c;

        public Union(A a)
        {
            type = typeof(A);
            this.a = a;
        }

        public Union(B b)
        {
            type = typeof(B);
            this.b = b;
        }

        public Union(C c)
        {
            type = typeof(C);
            this.c = c;
        }

        public bool Value(TypeTestSelector _)
        {
            return typeof(A) == type;
        }

        public bool Value(TypeTestSelector _)
        {
            return typeof(B) == type;
        }

        public bool Value(TypeTestSelector _)
        {
            return typeof(C) == type;
        }

        public A Value(GetValueTypeSelector _)
        {
            return a;
        }

        public B Value(GetValueTypeSelector _)
        {
            return b;
        }

        public C Value(GetValueTypeSelector _)
        {
            return c;
        }
    }

    public static class Is
    {
        public static TypeTestSelector OfType()
        {
            return null;
        }
    }

    public class TypeTestSelector
    {
    }

    public static class Get
    {
        public static GetValueTypeSelector ForType()
        {
            return null;
        }
    }

    public class GetValueTypeSelector
    {
    }

ভুল প্রকারের মান বের করার জন্য কোনও চেক নেই, যেমন:


var u = Union(10);
string s = u.Value(Get.ForType());

সুতরাং আপনি প্রয়োজনীয় পরীক্ষাগুলি যুক্ত বিবেচনা করতে পারেন এবং এই ক্ষেত্রে ব্যতিক্রম ছুঁড়ে দিতে পারেন।


-1

আমি ইউনিয়ন টাইপের নিজস্ব ব্যবহার করি।

এটি পরিষ্কার করার জন্য একটি উদাহরণ বিবেচনা করুন।

কল্পনা করুন আমাদের সাথে যোগাযোগের শ্রেণি রয়েছে:

public class Contact 
{
    public string Name { get; set; }
    public string EmailAddress { get; set; }
    public string PostalAdrress { get; set; }
}

এগুলি সবগুলি সরল স্ট্রিং হিসাবে সংজ্ঞায়িত করা হয়, তবে তারা কি কেবল স্ট্রিংস? অবশ্যই না. নামটিতে প্রথম নাম এবং শেষ নাম থাকতে পারে। অথবা একটি ইমেল কি কেবল চিহ্নগুলির একটি সেট? আমি জানি যে কমপক্ষে এটিতে @ থাকা উচিত এবং এটি অগত্যা।

আসুন আমাদের ডোমেন মডেলটি উন্নত করুন

public class PersonalName 
{
    public PersonalName(string firstName, string lastName) { ... }
    public string Name() { return _fistName + " " _lastName; }
}

public class EmailAddress 
{
    public EmailAddress(string email) { ... } 
}

public class PostalAdrress 
{
    public PostalAdrress(string address, string city, int zip) { ... } 
}

এই ক্লাসে তৈরি করার সময় বৈধতা থাকবে এবং শেষ পর্যন্ত আমাদের কাছে বৈধ মডেল থাকবে। পার্সোনা নাম ক্লাসে কনস্টাক্টর্টর একই সাথে ফার্স্টনাম এবং লাস্টনাম প্রয়োজন। এর অর্থ এই যে সৃষ্টির পরে এটির অবৈধ অবস্থা থাকতে পারে না।

এবং যথাক্রমে যোগাযোগ বর্গ

public class Contact 
{
    public PersonalName Name { get; set; }
    public EmailAdress EmailAddress { get; set; }
    public PostalAddress PostalAddress { get; set; }
}

এই ক্ষেত্রে আমাদের একই সমস্যা রয়েছে, যোগাযোগ শ্রেণীর অবজেক্টটি অবৈধ অবস্থায় থাকতে পারে। আমি বলতে চাইছি এটিতে ইমেল ঠিকানা থাকতে পারে তবে নাম নেই

var contact = new Contact { EmailAddress = new EmailAddress("foo@bar.com") };

আসুন এটি ঠিক করুন এবং কনস্ট্রাক্টরের সাথে পরিচিতি বর্গ তৈরি করুন যার জন্য ব্যক্তিগত নাম, ইমেল ঠিকানা এবং ডাক ঠিকানা প্রয়োজন:

public class Contact 
{
    public Contact(
               PersonalName personalName, 
               EmailAddress emailAddress,
               PostalAddress postalAddress
           ) 
    { 
         ... 
    }
}

তবে এখানে আমাদের আরও একটি সমস্যা রয়েছে। যদি ব্যক্তির কেবল ইমেল ঠিকানা থাকে এবং ডাক ঠিকানা না থাকে তবে কী হবে?

যদি আমরা সেখানে এটি নিয়ে চিন্তা করি তবে বুঝতে পারি যে যোগাযোগ শ্রেণীর অবজেক্টের বৈধ রাষ্ট্রের তিনটি সম্ভাবনা রয়েছে:

  1. একটি পরিচিতির কেবল একটি ইমেল ঠিকানা থাকে
  2. একটি পরিচিতির কেবল একটি ডাক ঠিকানা থাকে
  3. একটি পরিচিতির একটি ইমেল ঠিকানা এবং একটি ডাক ঠিকানা উভয়ই থাকে

আসুন ডোমেন মডেলগুলি লিখি। শুরুতে আমরা যোগাযোগ তথ্য শ্রেণি তৈরি করব যা উপরের কেসের সাথে সম্পর্কিত হবে state

public class ContactInfo 
{
    public ContactInfo(EmailAddress emailAddress) { ... }
    public ContactInfo(PostalAddress postalAddress) { ... }
    public ContactInfo(Tuple<EmailAddress,PostalAddress> emailAndPostalAddress) { ... }
}

এবং যোগাযোগ শ্রেণি:

public class Contact 
{
    public Contact(
              PersonalName personalName,
              ContactInfo contactInfo
           )
    {
        ...
    }
}

আসুন এটি ব্যবহার করার চেষ্টা করুন:

var contact = new Contact(
                  new PersonalName("James", "Bond"),
                  new ContactInfo(
                      new EmailAddress("agent@007.com")
                  )
               );
Console.WriteLine(contact.PersonalName()); // James Bond
Console.WriteLine(contact.ContactInfo().???) // here we have problem, because ContactInfo have three possible state and if we want print it we would write `if` cases

আসুন কন্টাক্টইনফো ক্লাসে ম্যাচ পদ্ধতি যুক্ত করুন

public class ContactInfo 
{
   // constructor 
   public TResult Match<TResult>(
                      Func<EmailAddress,TResult> f1,
                      Func<PostalAddress,TResult> f2,
                      Func<Tuple<EmailAddress,PostalAddress>> f3
                  )
   {
        if (_emailAddress != null) 
        {
             return f1(_emailAddress);
        } 
        else if(_postalAddress != null)
        {
             ...
        } 
        ...
   }
}

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

আসুন একটি সহায়ক ক্লাস তৈরি করুন, যাতে প্রতিটি সময় যতগুলি কোড না লিখেন।

public abstract class Union<T1,T2,T3>
    where T1 : class
    where T2 : class
    where T3 : class
{
    private readonly T1 _t1;
    private readonly T2 _t2;
    private readonly T3 _t3;
    public Union(T1 t1) { _t1 = t1; }
    public Union(T2 t2) { _t2 = t2; }
    public Union(T3 t3) { _t3 = t3; }

    public TResult Match<TResult>(
            Func<T1, TResult> f1,
            Func<T2, TResult> f2,
            Func<T3, TResult> f3
        )
    {
        if (_t1 != null)
        {
            return f1(_t1);
        }
        else if (_t2 != null)
        {
            return f2(_t2);
        }
        else if (_t3 != null)
        {
            return f3(_t3);
        }
        throw new Exception("can't match");
    }
}

ডেলিগেটস ফানক, অ্যাকশন এর সাথে যেমন করা হয় আমরা বিভিন্ন ধরণের জন্য আগে থেকেই এই জাতীয় শ্রেণি থাকতে পারি। ইউনিয়ন শ্রেণীর জন্য 4-6 জেনেরিক ধরণের পরামিতি পূর্ণ হবে।

ContactInfoক্লাসটি আবার লিখি :

public sealed class ContactInfo : Union<
                                     EmailAddress,
                                     PostalAddress,
                                     Tuple<EmaiAddress,PostalAddress>
                                  >
{
    public Contact(EmailAddress emailAddress) : base(emailAddress) { }
    public Contact(PostalAddress postalAddress) : base(postalAddress) { }
    public Contact(Tuple<EmaiAddress, PostalAddress> emailAndPostalAddress) : base(emailAndPostalAddress) { }
}

এখানে সংকলক কমপক্ষে একজন নির্মাণকারীর জন্য ওভাররাইড জিজ্ঞাসা করবে। আমরা যদি অন্য কন্সট্রাক্টরদের ওভাররাইড করতে ভুলে যাই তবে আমরা অন্য রাষ্ট্রের সাথে কন্টাক্টইনফো ক্লাসের অবজেক্ট তৈরি করতে পারি না। এটি ম্যাচের সময় রানটাইম ব্যতিক্রম থেকে আমাদের রক্ষা করবে।

var contact = new Contact(
                  new PersonalName("James", "Bond"),
                  new ContactInfo(
                      new EmailAddress("agent@007.com")
                  )
               );
Console.WriteLine(contact.PersonalName()); // James Bond
Console
    .WriteLine(
        contact
            .ContactInfo()
            .Match(
                (emailAddress) => emailAddress.Address,
                (postalAddress) => postalAddress.City + " " postalAddress.Zip.ToString(),
                (emailAndPostalAddress) => emailAndPostalAddress.Item1.Name + emailAndPostalAddress.Item2.City + " " emailAndPostalAddress.Item2.Zip.ToString()
            )
    );

এখানেই শেষ. আমি আপনি উপভোগ আশা করি.

মজা এবং লাভের জন্য সাইট এফ # থেকে নেওয়া উদাহরণ

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