INotifyPropertyChanged বাস্তবায়ন - এর চেয়ে ভালতর কোনও উপায় কি বিদ্যমান?


647

মাইক্রোসফ্টের উচিত ছিল INotifyPropertyChangedস্বয়ংক্রিয় বৈশিষ্ট্যগুলির মতো চটজলদি কিছু বাস্তবায়ন করা, কেবলমাত্র নির্দিষ্ট করে {get; set; notify;} আমি মনে করি এটি এটি করার জন্য অনেক অর্থবোধ করে। নাকি এটি করতে কোনও জটিলতা রয়েছে?

আমরা কি আমাদের সম্পত্তিগুলিতে 'বিজ্ঞপ্তি' জাতীয় কিছু বাস্তবায়ন করতে পারি? INotifyPropertyChangedআপনার শ্রেণিতে প্রয়োগের জন্য কি কোনও চমত্কার সমাধান রয়েছে বা এটি করার একমাত্র উপায় হ'ল PropertyChangedপ্রতিটি সম্পত্তিতে ইভেন্ট উত্থাপন ।

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



7
উপরের লিঙ্কটি মারা গেছে। github.com/SimonCropp/NotifyPropertyWeaver
Prime23

2
আপনি এর পরিবর্তে DependencyObject এবং DependencyProperties ব্যবহার করতে পারেন। হা! আমি একটি মজার করেছি।
ফিল


5
সি # তে পরিবর্তন করা যখন আমাদের আন্তঃনির্ভরশীলতার একটি বিশাল ব্যাক লগ ছিল তখন সম্ভব ছিল না। এমভিভিএম যখন জন্মগ্রহণ করেছিল তখন আমার অনুমান, আমরা সত্যিই এই সমস্যাটি সমাধানে খুব বেশি প্রচেষ্টা করতে পারি নি এবং আমি জানি যে প্যাটার্নস অ্যান্ড প্র্যাকটিসস টিমের কিছুটা পথ চলতে হয়েছিল (অতএব আপনিও তার অংশ হিসাবে এমইএফ পেয়েছিলেন) গবেষণা থ্রেড)। আজ আমি মনে করি [কলারমেমনারনেম] উপরের উত্তর।
স্কট বার্নস

উত্তর:


633

পোস্টশারপের মতো কিছু ব্যবহার না করে, আমি যে সর্বনিম্ন সংস্করণটি ব্যবহার করি তা ব্যবহার করে এমন কিছু ব্যবহার করে:

public class Data : INotifyPropertyChanged
{
    // boiler-plate
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
    protected bool SetField<T>(ref T field, T value, string propertyName)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) return false;
        field = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    // props
    private string name;
    public string Name
    {
        get { return name; }
        set { SetField(ref name, value, "Name"); }
    }
}

প্রতিটি সম্পত্তি তখন ঠিক এরকম কিছু:

    private string name;
    public string Name
    {
        get { return name; }
        set { SetField(ref name, value, "Name"); }
    }

যা বিশাল নয়; আপনি চাইলে এটি বেস-ক্লাস হিসাবেও ব্যবহার করা যেতে পারে। boolথেকে ফেরত SetFieldআপনি বলে যদি এটি একটি নো-অপ ছিল, যদি আপনি অন্য যুক্তিবিজ্ঞান প্রয়োগ করতে চান।


অথবা সি # 5 এর সাথে আরও সহজ:

protected bool SetField<T>(ref T field, T value,
    [CallerMemberName] string propertyName = null)
{...}

যাকে এরকম বলা যেতে পারে:

set { SetField(ref name, value); }

যা দিয়ে সংকলক "Name"স্বয়ংক্রিয়ভাবে যুক্ত হবে।


সি # 6.0 বাস্তবায়নকে আরও সহজ করে তোলে:

protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

... এবং এখন সি # 7 সহ:

protected void OnPropertyChanged(string propertyName)
   => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

protected bool SetField<T>(ref T field, T value,[CallerMemberName] string propertyName =  null)
{
    if (EqualityComparer<T>.Default.Equals(field, value)) return false;
    field = value;
    OnPropertyChanged(propertyName);
    return true;
}

private string name;
public string Name
{
    get => name;
    set => SetField(ref name, value);
}

4
দুর্দান্ত কৌশল! আমি সম্পত্তির নামের পরিবর্তে ল্যাম্বডা এক্সপ্রেশন ব্যবহার করার জন্য একটি উন্নতির পরামর্শ দিয়েছিলাম, আমার উত্তর দেখুন
টমাস লেভসেক

7
@ থমাস - ল্যাম্বদা সমস্ত ভাল এবং ভাল, তবে এটি এমন কোনও জিনিসের জন্য প্রচুর ওভারহেড যুক্ত করে যা আসলে খুব সহজ। একটি সহজ কৌশল, তবে আমি নিশ্চিত নই যে এটি সর্বদা ব্যবহারিক।
মার্ক Gravell

14
@ মার্ক - হ্যাঁ, এটি সম্ভবত পারফরম্যান্সকে হ্রাস করতে পারে ... তবে আমি সত্যিই এটি পছন্দ করি যে এটি সংকলনের সময় পরীক্ষা করা হয়েছিল এবং সঠিকভাবে "পুনর্নামকরণ" কমান্ড দ্বারা রিফ্যাক্ট করা হয়েছে
টমাস লেভেস্ক

4
@ গুডর সৌভাগ্যক্রমে, সি # 5 এর সাথে আপোস করার দরকার নেই - আপনি উভয়ের মধ্যে সেরাটি পেতে পারেন (পেড্রো 77 নোট হিসাবে)[CallerMemberName]
মার্ক গ্র্যাভেল

4
@ গুডোর ভাষা এবং কাঠামো পৃথক; আপনি সি # 5 সংকলকটি ব্যবহার করতে পারেন, লক্ষ্য। নেট 4, এবং অনুপস্থিত বৈশিষ্ট্যটি নিজে যুক্ত করুন - এটি দুর্দান্ত কাজ করবে। এটিতে সঠিক নাম থাকতে হবে এবং সঠিক নামস্থানে থাকতে হবে। এটি একটি নির্দিষ্ট সমাবেশে থাকার দরকার নেই।
মার্ক Gravell

196

নেট 4.5 হিসাবে শেষ পর্যন্ত এটি করার একটি সহজ উপায় আছে।

.NET 4.5 একটি নতুন কলার তথ্য বৈশিষ্ট্য উপস্থাপন করে।

private void OnPropertyChanged<T>([CallerMemberName]string caller = null) {
     // make sure only to call this if the value actually changes

     var handler = PropertyChanged;
     if (handler != null) {
        handler(this, new PropertyChangedEventArgs(caller));
     }
}

ফাংশনে একটি তুলনাকারী যুক্ত করা সম্ভবত একটি ভাল ধারণা।

EqualityComparer<T>.Default.Equals

আরও উদাহরণ এখানে এবং এখানে

কলারের তথ্যও দেখুন (সি # এবং ভিজ্যুয়াল বেসিক)


12
উজ্জ্বল! তবে কেন জেনেরিক?
abatishchev

@ বাতিষেভ আমার ধারণা এটি হওয়ার দরকার নেই, আমি এই অনুষ্ঠানটি সম্পত্তিটিও ঠিক রাখার ধারণার সাথে খেলছিলাম। আমি আমার উত্তরটি আপডেট করতে পারি কিনা তা সম্পূর্ণ সমাধানটি সরবরাহ করব see অতিরিক্ত উদাহরণগুলি ইতিমধ্যে একটি ভাল কাজ করে।
ড্যানিয়েল লিটল

3
এটি সি # 5.0 দ্বারা উপস্থাপিত হয়েছিল। নেট .৪.৪ এর সাথে এর কোনও যোগসূত্র নেই, তবে এটি দুর্দান্ত সমাধান!
জে লেনন

5
@J। লেনন .NET 4.5 এর সাথে এখনও কিছু করার আছে, সমস্ত বৈশিষ্ট্য কোথাও থেকে এসেছে এমএসডিএন.মাফিক সফটওয়্যার
ড্যানিয়েল লিটল

@ লাভিনস্কি আপনার অ্যাপ্লিকেশনটি যেমন .NET 3.5 এ পরিবর্তন করুন এবং দেখুন কীভাবে কাজ করবে দেখুন (vs2012-এ)
জে লেনন

162

আমি মার্কের সমাধানটি সত্যিই পছন্দ করি তবে আমার মনে হয় "ম্যাজিক স্ট্রিং" (যা রিফ্যাক্টরিং সমর্থন করে না) ব্যবহার এড়াতে এটিকে কিছুটা উন্নত করা যেতে পারে। স্ট্রিং হিসাবে সম্পত্তির নামটি ব্যবহার না করে এটিকে ল্যাম্বডা ভাবটি তৈরি করা সহজ:

private string name;
public string Name
{
    get { return name; }
    set { SetField(ref name, value, () => Name); }
}

মার্কের কোডে কেবল নিম্নলিখিত পদ্ধতিগুলি যুক্ত করুন, এটি কৌশলটি সম্পাদন করবে:

protected virtual void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
{
    if (selectorExpression == null)
        throw new ArgumentNullException("selectorExpression");
    MemberExpression body = selectorExpression.Body as MemberExpression;
    if (body == null)
        throw new ArgumentException("The body must be a member expression");
    OnPropertyChanged(body.Member.Name);
}

protected bool SetField<T>(ref T field, T value, Expression<Func<T>> selectorExpression)
{
    if (EqualityComparer<T>.Default.Equals(field, value)) return false;
    field = value;
    OnPropertyChanged(selectorExpression);
    return true;
}

বিটিডাব্লু, এটি এই ব্লগ পোস্ট আপডেট হওয়া ইউআরএল দ্বারা অনুপ্রাণিত হয়েছিল


6
এই পদ্ধতি ব্যবহার করে অন্তত একটি ফ্রেমওয়ার্ক এর ReactiveUI
আলস্কি

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

1
@ ব্রুনো ব্র্যান্ট আপনি কি নিশ্চিত যে কোনও পারফরম্যান্স হিট আছে? ব্লগ পোস্ট অনুসারে প্রতিচ্ছবি রানটাইম (অর্থাত্ স্ট্যাটিক প্রতিবিম্ব) চেয়ে সংকলনের সময় ঘটে happens
নাথানিয়েল এলকিন্স 21

6
আমি বিশ্বাস করি যে আপনার পুরো অনপ্রোটেরচেন্জড <টি> সি # 6 এর নেমোফ অপারেটরের সাথে অচল, এই দানবটিকে কিছুটা স্লিকার করে।
ট্রুবেনফুচস

5
@ ট্রাউবেনফুচস, আসলে, সি # 5 এর কলার মেমারনাম এট্রিবিউটটি এটিকে আরও সহজ করে তুলেছে, যেহেতু আপনার কোনও কিছুই পাস করার দরকার নেই ...
টমাস লেভস্ক

120

এখানেও ফডি রয়েছে যার একটি প্রপার্টি চেঞ্জড অ্যাড-ইন রয়েছে, যা আপনাকে এটি লিখতে দেয়:

[ImplementPropertyChanged]
public class Person 
{        
    public string GivenNames { get; set; }
    public string FamilyName { get; set; }
}

... এবং সংকলন সময়ে সম্পত্তি পরিবর্তিত বিজ্ঞপ্তি ইনজেকশন।


7
আমার মনে হয় ওপি ঠিক এটিই খুঁজছিল যখন তারা জিজ্ঞাসা করল "আমরা কীভাবে আমাদের সম্পত্তিগুলিতে 'নোটিফিকেশন' জাতীয় কিছু বাস্তবায়ন করতে পারি? আপনার শ্রেণিতে INotifyPropertyChanged বাস্তবায়নের জন্য কি কোনও সুন্দর সমাধান রয়েছে"
আশোয়াত

3
এটি সত্যই একমাত্র কৌতূহলী সমাধান এবং @ সিএডিব্লোক যেমন বলেছেন তেমন নির্দোষভাবে এটি কাজ করে। এবং আমি তাঁতি সম্পর্কেও সংশয়ী ছিলাম, তবে আমি পিছনে আইএল কোডটি চেক / পুনরায় পরীক্ষা করেছি এবং এটি নিখুঁত, এটি সহজ, আপনার প্রয়োজনীয় সমস্ত কি এবং অন্য কোনওটি নয়। এটি নোটিফোনঅনপ্রপ ..., অননোটাইফাই ... তাতে কিছু আসে যায় না, তাই আপনার যে কোনও বেস শ্রেণীর সাথে ভাল কাজ করে যা আইএনটিফাই প্রয়োগ করে .. ।
এনএসগাগা-বেশিরভাগ-নিষ্ক্রিয়

1
তাঁতি কী করছে তা আপনি সহজেই ডাবল-চেক করতে পারেন, বিল্ড আউটপুট উইন্ডোটি একবার দেখুন, এটি সমস্ত বিভাজনযুক্ত প্রপার্টিচেন্জড জিনিসের তালিকা করে। রেগেক্স প্যাটার্ন সহ ভিএসক্লোরআউটপুট এক্সটেনশন ব্যবহার করে "Fody/.*?:",LogCustom2,Trueএটি "কাস্টম 2" রঙে হাইলাইট করে। আমি এটি উজ্জ্বল গোলাপী তৈরি করেছি যাতে এটি সন্ধান করা সহজ। কেবলমাত্র সবকিছুই ফডি করুন, প্রচুর পুনরাবৃত্তি টাইপিং রয়েছে এমন কোনও কিছু করার এটি সর্বতমতম উপায়।
সিএডি

@ মমমুদনেজারসারন না, এটি নয়, আমি মনে করি এটির কনফিগার করার পদ্ধতিতে কিছুটা পরিবর্তন হয়েছিল তবে ফডি প্রপার্টি চেঞ্জড এখনও জীবিত এবং সক্রিয় রয়েছে।
ল্যারি

65

আমি মনে করি মানুষের পারফরম্যান্সের দিকে একটু বেশি মনোযোগ দেওয়া উচিত; অনেকগুলি অবজেক্ট আবদ্ধ হওয়া (10,000+ সারি সহ গ্রিডের কথা ভাবা) বা যদি বস্তুর মান ঘন ঘন পরিবর্তিত হয় (রিয়েল-টাইম মনিটরিং অ্যাপ্লিকেশন) তখন এটি সত্যই ইউআইকে প্রভাবিত করে।

আমি এখানে এবং অন্য কোথাও পাওয়া বিভিন্ন বাস্তবায়ন নিয়েছি এবং একটি তুলনা করেছি; INotifyPropertyChanged বাস্তবায়নগুলির পারফরম্যান্স তুলনা এটি পরীক্ষা করে দেখুন ।


এখানে ফলাফল তাকান ইমপ্লিমেশন বনাম রানটাইম


14
-1: ওভারহেডের কোনও পারফরম্যান্স নেই: সংকলনের সময় CallerMemberName আক্ষরিক মানগুলিতে পরিবর্তিত হয়। আপনার অ্যাপ্লিকেশনটি চেষ্টা করুন এবং ডিকম্পাইল করুন।
JYL

: এখানে অনুযায়ী প্রশ্ন ও উত্তর stackoverflow.com/questions/22580623/...
uli78

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

1
আপনার যদি ইউআইতে 10,000+ এর গ্রিড থাকে তবে আপনার সম্ভবত সম্পাদনা পরিচালনা করার জন্য পদ্ধতির সমন্বয় করা উচিত, যেমন পেজিংয়ের যেখানে আপনি কেবল প্রতি পৃষ্ঠায় 10, 50, 100, 250 হিট দেখান ...
অস্টিন রাইমার

অস্টিন রাইমার, যদি আপনার কাছে লার্জ ডেটা থাকে + 50 ব্যবহার করে ডেটা ভার্চুয়ালাইজেশন সমস্ত ডেটা লোড করার প্রয়োজন নেই এটি কেবলমাত্র বর্তমান ডেটা লোড করবে যা বর্তমান স্কোলিং প্রদর্শিত অঞ্চলটিতে দৃশ্যমান!
বিলাল

38

আমি আমার ব্লগে http://timoch.com/blog/2013/08/annoyed-with-inotifypropertychange/ এ বাইন্ডেবল ক্লাস প্রবর্তন করি বিন্দেবল একটি সম্পত্তি ব্যাগ হিসাবে একটি অভিধান ব্যবহার করে। রেফ প্যারামিটারগুলি ব্যবহার করে নিজস্ব ব্যাকিং ফিল্ড পরিচালনা করতে সাবক্লাসের জন্য প্রয়োজনীয় ওভারলোডগুলি যুক্ত করা যথেষ্ট সহজ।

  • কোন ম্যাজিক স্ট্রিং নেই
  • প্রতিবিম্ব নেই
  • ডিফল্ট অভিধানের দাবীটি দমন করতে উন্নত করা যায়

কোড:

public class Bindable : INotifyPropertyChanged {
    private Dictionary<string, object> _properties = new Dictionary<string, object>();

    /// <summary>
    /// Gets the value of a property
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="name"></param>
    /// <returns></returns>
    protected T Get<T>([CallerMemberName] string name = null) {
        Debug.Assert(name != null, "name != null");
        object value = null;
        if (_properties.TryGetValue(name, out value))
            return value == null ? default(T) : (T)value;
        return default(T);
    }

    /// <summary>
    /// Sets the value of a property
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="value"></param>
    /// <param name="name"></param>
    /// <remarks>Use this overload when implicitly naming the property</remarks>
    protected void Set<T>(T value, [CallerMemberName] string name = null) {
        Debug.Assert(name != null, "name != null");
        if (Equals(value, Get<T>(name)))
            return;
        _properties[name] = value;
        OnPropertyChanged(name);
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

এটি এর মতো ব্যবহার করা যেতে পারে:

public class Contact : Bindable {
    public string FirstName {
        get { return Get<string>(); }
        set { Set(value); }
    }
}

2
এটি একটি দুর্দান্ত সমাধান, তবে কেবলমাত্র খারাপ দিকটি হ'ল বক্সিং / আনবক্সিং জড়িত একটি ছোট পারফরম্যান্স হিট।
এমগিটল 21

1
আমি সেটটি ব্যবহার করতে protected T Get<T>(T defaultValue, [CallerMemberName] string name = null)এবং চেক করতে পরামর্শ দেব if (_properties.ContainsKey(name) && Equals(value, Get<T>(default(T), name)))(ডিফল্ট মান হিসাবে সেট করার সময় উত্থাপন ও সংরক্ষণের জন্য)
মিকেল

1
@ কাস্টম ডিফল্ট মানগুলির জন্য মিকুয়েল যোগ করা নিশ্চিতভাবে কার্যকর হতে পারে, তবে মানটি পরিবর্তিত হলে কেবল পরিবর্তিত ইভেন্ট বাড়ানোর ক্ষেত্রে আপনার যত্নবান হওয়া উচিত। সম্পত্তি হিসাবে একই মান নির্ধারণের ফলে ইভেন্টগুলি বাড়াতে হবে না। আমাকে অবশ্যই বেশিরভাগ ক্ষেত্রে এটি নিরীহ হিসাবে স্বীকার করতে হবে, তবে আমি ইউআই প্রতিক্রিয়াশীলতা ধ্বংসকারী ঘটনাসমূহের সাথে সম্পত্তিগুলিতে হাজার হাজার সময় একই মূল্যের জন্য সেট করা বেশ কিছুটা সময় হয়েছি।
টিমোচ

1
@ স্টাকাক্সের আমার কাছে কয়েকটি অ্যাপ্লিকেশন রয়েছে যা পূর্বাবস্থায় / পূর্বাবস্থায় ফেলার জন্য স্মৃতিসৌধের নকশাকে সমর্থন করতে বা নিবার্নেট ব্যবহারযোগ্য নয় এমন অ্যাপ্লিকেশনগুলিতে কাজের প্যাটার্নের ইউনিট সক্ষম করার জন্য এটি তৈরি করেছে
টিমোচ

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

15

আমার কাছে এখনও এটি চেষ্টা করার সুযোগ নেই তবে আমি পরের বার INOTifyPropertyChanged এর জন্য একটি বড় প্রয়োজন সহ একটি প্রকল্প স্থাপন করছি আমি একটি পোস্টশার্প বৈশিষ্ট্য লিখতে চাইছি যা সংকলনের সময় কোডটি ইনজেক্ট করবে। কিছুটা এইরকম:

[NotifiesChange]
public string FirstName { get; set; }

হয়ে যাবে:

private string _firstName;

public string FirstName
{
   get { return _firstname; }
   set
   {
      if (_firstname != value)
      {
          _firstname = value;
          OnPropertyChanged("FirstName")
      }
   }
}

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

বর্তমানে আমি রিশার্পারে একটি কাস্টম টেম্পলেট ব্যবহার করছি, তবে এর সাথেও আমি আমার সমস্ত সম্পত্তি এত দীর্ঘায়িত হতে বিরক্ত হয়ে যাচ্ছি।


আহ, একটি দ্রুত গুগল সার্চ (আগে আমি এই লিখেছিলেন যা আমি করেছি উচিত) অনুষ্ঠান অন্তত এক ব্যক্তি সামনে ভালো কিছু করেছে এখানে । আমার মনে যা ছিল ঠিক তা নয়, তত্ত্বটি ভাল তা দেখানোর জন্য যথেষ্ট কাছে।


6
ফডি নামে একটি নিখরচায় সরঞ্জাম একই জিনিসটি দেখে মনে হয়, জেনেরিক সংকলন-সময় কোড ইনজেক্টর হিসাবে কাজ করে। এটি নুগেটে ডাউনলোডযোগ্য, যেমন এটির প্রপার্টি চেঞ্জড এবং প্রোপার্টি চেঞ্জিং প্লাগইন প্যাকেজগুলি রয়েছে।
ট্রায়ঙ্কো

11

হ্যাঁ, আরও ভাল উপায় অবশ্যই বিদ্যমান। এটা এখানে:

এই দরকারী নিবন্ধের ভিত্তিতে ধাপে ধাপে টিউটোরিয়াল আমার দ্বারা সঙ্কুচিত ।

  • নতুন প্রকল্প তৈরি করুন
  • প্রকল্পে কাসল কোর প্যাকেজ ইনস্টল করুন

ইনস্টল-প্যাকেজ ক্যাসেল.কোর

  • শুধুমাত্র এমভিভিএম হালকা লাইব্রেরি ইনস্টল করুন

ইনস্টল-প্যাকেজ এমভিভিএমলাইটলিব্স

  • প্রকল্পে দুটি ক্লাস যুক্ত করুন:

NotifierInterceptor

public class NotifierInterceptor : IInterceptor
    {
        private PropertyChangedEventHandler handler;
        public static Dictionary<String, PropertyChangedEventArgs> _cache =
          new Dictionary<string, PropertyChangedEventArgs>();

        public void Intercept(IInvocation invocation)
        {
            switch (invocation.Method.Name)
            {
                case "add_PropertyChanged":
                    handler = (PropertyChangedEventHandler)
                              Delegate.Combine(handler, (Delegate)invocation.Arguments[0]);
                    invocation.ReturnValue = handler;
                    break;
                case "remove_PropertyChanged":
                    handler = (PropertyChangedEventHandler)
                              Delegate.Remove(handler, (Delegate)invocation.Arguments[0]);
                    invocation.ReturnValue = handler;
                    break;
                default:
                    if (invocation.Method.Name.StartsWith("set_"))
                    {
                        invocation.Proceed();
                        if (handler != null)
                        {
                            var arg = retrievePropertyChangedArg(invocation.Method.Name);
                            handler(invocation.Proxy, arg);
                        }
                    }
                    else invocation.Proceed();
                    break;
            }
        }

        private static PropertyChangedEventArgs retrievePropertyChangedArg(String methodName)
        {
            PropertyChangedEventArgs arg = null;
            _cache.TryGetValue(methodName, out arg);
            if (arg == null)
            {
                arg = new PropertyChangedEventArgs(methodName.Substring(4));
                _cache.Add(methodName, arg);
            }
            return arg;
        }
    }

ProxyCreator

public class ProxyCreator
{
    public static T MakeINotifyPropertyChanged<T>() where T : class, new()
    {
        var proxyGen = new ProxyGenerator();
        var proxy = proxyGen.CreateClassProxy(
          typeof(T),
          new[] { typeof(INotifyPropertyChanged) },
          ProxyGenerationOptions.Default,
          new NotifierInterceptor()
          );
        return proxy as T;
    }
}
  • আপনার দর্শন মডেল তৈরি করুন, উদাহরণস্বরূপ:

-

 public class MainViewModel
    {
        public virtual string MainTextBox { get; set; }

        public RelayCommand TestActionCommand
        {
            get { return new RelayCommand(TestAction); }
        }

        public void TestAction()
        {
            Trace.WriteLine(MainTextBox);
        }
    }
  • এক্সএএমএলে বাইন্ডিংগুলি রাখুন:

    <TextBox Text="{Binding MainTextBox}" ></TextBox>
    <Button Command="{Binding TestActionCommand}" >Test</Button>
  • কোডের পিছনে ফাইল মেইনওয়াইন্ডো.এক্স্যামল সি-তে কোডের লাইন রাখুন:

DataContext = ProxyCreator.MakeINotifyPropertyChanged<MainViewModel>();

  • উপভোগ করুন।

এখানে চিত্র বর্ণনা লিখুন

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


আপনি ক্যাসলের কোন সংস্করণ ব্যবহার করছেন তা জানতে আগ্রহী। আমি 3.3.0 ব্যবহার করছি এবং CreateClassProxy পদ্ধতি যারা পরামিতি নেই: type, interfaces to apply, interceptors
আইএ্যাবস্ট্রাক্ট

কিছু নয়, আমি জেনেরিক CreateClassProxy<T>পদ্ধতিটি ব্যবহার করছিলাম । অনেক আলাদা ... হুম, ভাবছেন কেন জেনেরিক পদ্ধতির সাথে এতটা সীমাবদ্ধ। :(
আই এ্যাবস্ট্রাক্ট

7

একটি খুব এওপি-এর মত পদ্ধতিরটি হ'ল ফ্লাইতে ইতিমধ্যে ইনস্ট্যান্টেশনযুক্ত বস্তুর উপর INotifyPropertyChanged স্টাফ ইনজেক্ট করা। আপনি ক্যাসল ডায়নামিকপ্রক্সির মতো কিছু দিয়ে এটি করতে পারেন। কৌশলটি ব্যাখ্যা করে এখানে একটি নিবন্ধ দেওয়া হয়েছে:

একটি বিদ্যমান বস্তুতে INotifyPropertyChanged যোগ করা


5

এখানে দেখুন: http://dotnet-forum.de/blogs/thearchitect/archive/2012/11/01/die-optimale-implementierung-des-inotifypropertychanged-interfaces.aspx

এটি জার্মান ভাষায় লেখা, তবে আপনি ভিউমোডেলবেস.সি. ডাউনলোড করতে পারেন। সিএস-ফাইলের সমস্ত মন্তব্য ইংরেজিতে লেখা।

এই ভিউমোডেলবেস-ক্লাসের মাধ্যমে সুপরিচিত নির্ভরশীল বৈশিষ্ট্যগুলির অনুরূপ বাইন্ডেবল সম্পত্তিগুলি প্রয়োগ করা সম্ভব:

public string SomeProperty
{
    get { return GetValue( () => SomeProperty ); }
    set { SetValue( () => SomeProperty, value ); }
}

1
লিঙ্কটি নষ্ট হয়ে গেছে।
গুগ

4

টমাসের উত্তরের ভিত্তিতে যা মার্কের উত্তর থেকে অভিযোজিত হয়েছিল আমি প্রতিফলিত সম্পত্তি পরিবর্তিত কোডটিকে বেস শ্রেণিতে পরিণত করেছি:

public abstract class PropertyChangedBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) 
            handler(this, new PropertyChangedEventArgs(propertyName));
    }

    protected void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
    {
        if (selectorExpression == null)
            throw new ArgumentNullException("selectorExpression");
        var me = selectorExpression.Body as MemberExpression;

        // Nullable properties can be nested inside of a convert function
        if (me == null)
        {
            var ue = selectorExpression.Body as UnaryExpression;
            if (ue != null)
                me = ue.Operand as MemberExpression;
        }

        if (me == null)
            throw new ArgumentException("The body must be a member expression");

        OnPropertyChanged(me.Member.Name);
    }

    protected void SetField<T>(ref T field, T value, Expression<Func<T>> selectorExpression, params Expression<Func<object>>[] additonal)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) return;
        field = value;
        OnPropertyChanged(selectorExpression);
        foreach (var item in additonal)
            OnPropertyChanged(item);
    }
}

থমাসের জবাব হিসাবে ব্যবহারটি হ'ল বাদে আপনি অতিরিক্ত বৈশিষ্ট্যগুলি জানাতে পারেন। গণিত কলামগুলি হ্যান্ডেল করার জন্য এটি প্রয়োজনীয় ছিল যা গ্রিডে রিফ্রেশ করা দরকার।

private int _quantity;
private int _price;

public int Quantity 
{ 
    get { return _quantity; } 
    set { SetField(ref _quantity, value, () => Quantity, () => Total); } 
}
public int Price 
{ 
    get { return _price; } 
    set { SetField(ref _price, value, () => Price, () => Total); } 
}
public int Total { get { return _price * _quantity; } }

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


4

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

এটি একবার বেস ক্লাসে INotifyPropertyChanged প্রয়োগ করতে এবং তারপরে নিম্নলিখিত শৈলীতে উদ্ভূত শ্রেণি ঘোষণা করে, এখনও নতুন বৈশিষ্ট্যের জন্য INotifyPropertyChanged সমর্থন করে:

public class Animal:Concept
{
    protected Animal(){}
    public virtual string Name { get; set; }
    public virtual int Age { get; set; }
}

উত্পন্ন শ্রেণীর জটিলতা বা প্রক্সি নির্মাণ নিম্নলিখিত লাইনের পিছনে লুকানো যেতে পারে:

var animal = Concept.Create<Animal>.New();

এবং সমস্ত INotifyPropertyChanged বাস্তবায়ন কাজ এইভাবে করা যেতে পারে:

public class Concept:INotifyPropertyChanged
{
    //Hide constructor
    protected Concept(){}

    public static class Create<TConcept> where TConcept:Concept
    {
        //Construct derived Type calling PropertyProxy.ConstructType
        public static readonly Type Type = PropertyProxy.ConstructType<TConcept, Implementation<TConcept>>(new Type[0], true);
        //Create constructing delegate calling Constructor.Compile
        public static Func<TConcept> New = Constructor.Compile<Func<TConcept>>(Type);
    }


    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(PropertyChangedEventArgs eventArgs)
    {
        var caller = PropertyChanged;
        if(caller!=null)
        {
            caller(this, eventArgs);
        }
    }

    //define implementation
    public class Implementation<TConcept> : DefaultImplementation<TConcept> where TConcept:Concept
    {
        public override Func<TBaseType, TResult> OverrideGetter<TBaseType, TDeclaringType, TConstructedType, TResult>(PropertyInfo property)
        {
            return PropertyImplementation<TBaseType, TDeclaringType>.GetGetter<TResult>(property.Name);
        }
        /// <summary>
        /// Overriding property setter implementation.
        /// </summary>
        /// <typeparam name="TBaseType">Base type for implementation. TBaseType must be TConcept, and inherits all its constraints. Also TBaseType is TDeclaringType.</typeparam>
        /// <typeparam name="TDeclaringType">Type, declaring property.</typeparam>
        /// <typeparam name="TConstructedType">Constructed type. TConstructedType is TDeclaringType and TBaseType.</typeparam>
        /// <typeparam name="TResult">Type of property.</typeparam>
        /// <param name="property">PropertyInfo of property.</param>
        /// <returns>Delegate, corresponding to property setter implementation.</returns>
        public override Action<TBaseType, TResult> OverrideSetter<TBaseType, TDeclaringType, TConstructedType, TResult>(PropertyInfo property)
        {
            //This code called once for each declared property on derived type's initialization.
            //EventArgs instance is shared between all events for each concrete property.
            var eventArgs = new PropertyChangedEventArgs(property.Name);
            //get delegates for base calls.
            Action<TBaseType, TResult> setter = PropertyImplementation<TBaseType, TDeclaringType>.GetSetter<TResult>(property.Name);
            Func<TBaseType, TResult> getter = PropertyImplementation<TBaseType, TDeclaringType>.GetGetter<TResult>(property.Name);

            var comparer = EqualityComparer<TResult>.Default;

            return (pthis, value) =>
            {//This code executes each time property setter is called.
                if (comparer.Equals(value, getter(pthis))) return;
                //base. call
                setter(pthis, value);
                //Directly accessing Concept's protected method.
                pthis.OnPropertyChanged(eventArgs);
            };
        }
    }
}

এটি রিফ্যাক্টরিংয়ের জন্য পুরোপুরি নিরাপদ, ধরণের নির্মাণের পরে কোনও প্রতিবিম্ব ব্যবহার করে না এবং পর্যাপ্ত দ্রুত।


আপনার কেন TDeclarationটাইপ প্যারামিটার দরকার PropertyImplementation? অবশ্যই আপনি কল দিয়ে উপযুক্ত কলটি পাবেন (কলভার্ট নয়) কেবলমাত্র প্রেরক / সেটটার দিয়ে TImplementation?
অ্যান্ড্রু সাবিনিখ

টিমিপ্লিমেশন বেশিরভাগ ক্ষেত্রে কাজ করে। ব্যতিক্রমগুলি হ'ল: ১ "নতুন" সি # কীওয়ার্ড দিয়ে নতুন সংজ্ঞাযুক্ত বৈশিষ্ট্য। 2. সুস্পষ্ট ইন্টারফেস প্রয়োগের বৈশিষ্ট্য।
কেলকোলিন

3

এই সমস্ত উত্তর খুব সুন্দর।

আমার সমাধানটি কোডটি স্নিপেট ব্যবহার করে কাজটি করছে।

এটি প্রপার্টি চেঞ্জড ইভেন্টের সহজ কল ব্যবহার করে।

এই স্নিপেটটি সংরক্ষণ করুন এবং আপনি 'ফুলপ্রপ' স্নিপেট ব্যবহার করার সাথে এটি ব্যবহার করুন।

অবস্থানটি ভিজুয়াল স্টুডিওতে 'সরঞ্জাম \ কোড স্নিপেট ম্যানেজার ...' মেনুতে পাওয়া যাবে।

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Title>inotifypropfull</Title>
            <Shortcut>inotifypropfull</Shortcut>
            <HelpUrl>http://ofirzeitoun.wordpress.com/</HelpUrl>
            <Description>Code snippet for property and backing field with notification</Description>
            <Author>Ofir Zeitoun</Author>
            <SnippetTypes>
                <SnippetType>Expansion</SnippetType>
            </SnippetTypes>
        </Header>
        <Snippet>
            <Declarations>
                <Literal>
                    <ID>type</ID>
                    <ToolTip>Property type</ToolTip>
                    <Default>int</Default>
                </Literal>
                <Literal>
                    <ID>property</ID>
                    <ToolTip>Property name</ToolTip>
                    <Default>MyProperty</Default>
                </Literal>
                <Literal>
                    <ID>field</ID>
                    <ToolTip>The variable backing this property</ToolTip>
                    <Default>myVar</Default>
                </Literal>
            </Declarations>
            <Code Language="csharp">
                <![CDATA[private $type$ $field$;

    public $type$ $property$
    {
        get { return $field$;}
        set { 
            $field$ = value;
            var temp = PropertyChanged;
            if (temp != null)
            {
                temp(this, new PropertyChangedEventArgs("$property$"));
            }
        }
    }
    $end$]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>

আপনি নিজের পছন্দ মতো কলটি পরিবর্তন করতে পারেন (উপরের সমাধানগুলি ব্যবহার করতে)


2

যদি আপনি .NET 4.5 এ গতিশীলতা ব্যবহার করে থাকেন তবে আপনার উদ্বিগ্ন হওয়ার দরকার নেই INotifyPropertyChanged

dynamic obj = new ExpandoObject();
obj.Name = "John";

যদি নাম কিছু নিয়ন্ত্রণে আবদ্ধ থাকে তবে তা ঠিক কাজ করে।


1
এটি ব্যবহারের কোনও অসুবিধা?
juFo

2

আর একটি সম্মিলিত সমাধান স্ট্যাকফ্রেম ব্যবহার করছে:

public class BaseViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void Set<T>(ref T field, T value)
    {
        MethodBase method = new StackFrame(1).GetMethod();
        field = value;
        Raise(method.Name.Substring(4));
    }

    protected void Raise(string propertyName)
    {
        var temp = PropertyChanged;
        if (temp != null)
        {
            temp(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

ব্যবহার:

public class TempVM : BaseViewModel
{
    private int _intP;
    public int IntP
    {
        get { return _intP; }
        set { Set<int>(ref _intP, value); }
    }
}

2
এটা কি দ্রুত? স্ট্যাক ফ্রেমে অ্যাক্সেস কিছু অনুমতি প্রয়োজনীয়তা আবদ্ধ না? অ্যাসিঙ্ক / অপেক্ষা করার প্রসঙ্গে কি সেই শক্তিশালী?
স্টাফেন গ্যারিচন

@ স্টাফেনগৌরিচন না, এটি নেই। স্ট্যাক ফ্রেম অ্যাক্সেস মানে বেশিরভাগ ক্ষেত্রে যথেষ্ট পারফরম্যান্স হিট।
ব্রুনো ব্র্যান্ট

হ্যাঁ এটি আছে, আপনি এটি কোডের ভিউতে
অফির

নোট করুন যে ইনলাইনিং get_Fooপদ্ধতিটি রিলিজ মোডে লুকিয়ে রাখতে পারে ।
bytecode77

2

আমি পুনরায় ব্যবহারের জন্য আমার বেস লাইব্রেরিতে একটি এক্সটেনশন পদ্ধতি তৈরি করেছি:

public static class INotifyPropertyChangedExtensions
{
    public static bool SetPropertyAndNotify<T>(this INotifyPropertyChanged sender,
               PropertyChangedEventHandler handler, ref T field, T value, 
               [CallerMemberName] string propertyName = "",
               EqualityComparer<T> equalityComparer = null)
    {
        bool rtn = false;
        var eqComp = equalityComparer ?? EqualityComparer<T>.Default;
        if (!eqComp.Equals(field,value))
        {
            field = value;
            rtn = true;
            if (handler != null)
            {
                var args = new PropertyChangedEventArgs(propertyName);
                handler(sender, args);
            }
        }
        return rtn;
    }
}

CallerMemberNameAttribute এর কারণে এটি নেট 4.5 নিয়ে কাজ করে । আপনি যদি এটি পূর্ববর্তী। নেট সংস্করণ দিয়ে ব্যবহার করতে চান তবে আপনার থেকে পদ্ধতি ঘোষণাটি পরিবর্তন করতে হবে:...,[CallerMemberName] string propertyName = "", ... করতে হবে...,string propertyName, ...

ব্যবহার:

public class Dog : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    string _name;

    public string Name
    {
        get { return _name; }
        set
        {
            this.SetPropertyAndNotify(PropertyChanged, ref _name, value);
        }
    }
}

2

আমি এই পথে সমাধান করেছি (এটি সামান্য শ্রমজীবী ​​তবে এটি রানটাইমের ক্ষেত্রে অবশ্যই দ্রুত)।

ভিবিতে (দুঃখিত, তবে আমি মনে করি এটি সি # তে এটি অনুবাদ করা শক্ত নয়), আমি আরই দিয়ে এই প্রতিস্থাপনটি তৈরি করেছি:

(?<Attr><(.*ComponentModel\.)Bindable\(True\)>)( |\r\n)*(?<Def>(Public|Private|Friend|Protected) .*Property )(?<Name>[^ ]*) As (?<Type>.*?)[ |\r\n](?![ |\r\n]*Get)

সঙ্গে:

Private _${Name} As ${Type}\r\n${Attr}\r\n${Def}${Name} As ${Type}\r\nGet\r\nReturn _${Name}\r\nEnd Get\r\nSet (Value As ${Type})\r\nIf _${Name} <> Value Then \r\n_${Name} = Value\r\nRaiseEvent PropertyChanged(Me, New ComponentModel.PropertyChangedEventArgs("${Name}"))\r\nEnd If\r\nEnd Set\r\nEnd Property\r\n

এই ট্রান্সফর্মটি এর মতো সমস্ত কোড:

<Bindable(True)>
Protected Friend Property StartDate As DateTime?

ভিতরে

Private _StartDate As DateTime?
<Bindable(True)>
Protected Friend Property StartDate As DateTime?
    Get
        Return _StartDate
    End Get
    Set(Value As DateTime?)
        If _StartDate <> Value Then
            _StartDate = Value
            RaiseEvent PropertyChange(Me, New ComponentModel.PropertyChangedEventArgs("StartDate"))
        End If
    End Set
End Property

এবং যদি আমি আরও বেশি পঠনযোগ্য কোড পেতে চাই তবে আমি নীচের বিকল্পটি তৈরির বিপরীতে হতে পারি:

Private _(?<Name>.*) As (?<Type>.*)[\r\n ]*(?<Attr><(.*ComponentModel\.)Bindable\(True\)>)[\r\n ]*(?<Def>(Public|Private|Friend|Protected) .*Property )\k<Name> As \k<Type>[\r\n ]*Get[\r\n ]*Return _\k<Name>[\r\n ]*End Get[\r\n ]*Set\(Value As \k<Type>\)[\r\n ]*If _\k<Name> <> Value Then[\r\n ]*_\k<Name> = Value[\r\n ]*RaiseEvent PropertyChanged\(Me, New (.*ComponentModel\.)PropertyChangedEventArgs\("\k<Name>"\)\)[\r\n ]*End If[\r\n ]*End Set[\r\n ]*End Property

সঙ্গে

${Attr} ${Def} ${Name} As ${Type}

আমি সেট পদ্ধতির আইএল কোডটি প্রতিস্থাপন করতে নিক্ষেপ করি, তবে আমি আইএল-তে প্রচুর সংকলিত কোড লিখতে পারি না ... যদি কোনও দিন আমি এটি লিখি, আমি আপনাকে বলব!


2

আমি এটিকে স্নিপেট হিসাবে রাখি। সি # 6 হ্যান্ডলারটি চাওয়ার জন্য কিছু সুন্দর বাক্য গঠন যুক্ত করেছে।

// INotifyPropertyChanged

public event PropertyChangedEventHandler PropertyChanged;

private void Set<T>(ref T property, T value, [CallerMemberName] string propertyName = null)
{
    if (EqualityComparer<T>.Default.Equals(property, value) == false)
    {
        property = value;
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

2

এখানে নোটিফাইপ্রোপার্টিচেনজডের ইউনিটি 3 ডি বা নন-কলার মেমারনাম সংস্করণ

public abstract class Bindable : MonoBehaviour, INotifyPropertyChanged
{
    private readonly Dictionary<string, object> _properties = new Dictionary<string, object>();
    private static readonly StackTrace stackTrace = new StackTrace();
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    ///     Resolves a Property's name from a Lambda Expression passed in.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="property"></param>
    /// <returns></returns>
    internal string GetPropertyName<T>(Expression<Func<T>> property)
    {
        var expression = (MemberExpression) property.Body;
        var propertyName = expression.Member.Name;

        Debug.AssertFormat(propertyName != null, "Bindable Property shouldn't be null!");
        return propertyName;
    }

    #region Notification Handlers

    /// <summary>
    ///     Notify's all other objects listening that a value has changed for nominated propertyName
    /// </summary>
    /// <param name="propertyName"></param>
    internal void NotifyOfPropertyChange(string propertyName)
    {
        OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }

    /// <summary>
    ///     Notifies subscribers of the property change.
    /// </summary>
    /// <typeparam name="TProperty">The type of the property.</typeparam>
    /// <param name="property">The property expression.</param>
    internal void NotifyOfPropertyChange<TProperty>(Expression<Func<TProperty>> property)
    {
        var propertyName = GetPropertyName(property);
        NotifyOfPropertyChange(propertyName);
    }

    /// <summary>
    ///     Raises the <see cref="PropertyChanged" /> event directly.
    /// </summary>
    /// <param name="e">The <see cref="PropertyChangedEventArgs" /> instance containing the event data.</param>
    internal void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, e);
        }
    }

    #endregion

    #region Getters

    /// <summary>
    ///     Gets the value of a property
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="name"></param>
    /// <returns></returns>
    internal T Get<T>(Expression<Func<T>> property)
    {
        var propertyName = GetPropertyName(property);
        return Get<T>(GetPropertyName(property));
    }

    /// <summary>
    ///     Gets the value of a property automatically based on its caller.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    internal T Get<T>()
    {
        var name = stackTrace.GetFrame(1).GetMethod().Name.Substring(4); // strips the set_ from name;
        return Get<T>(name);
    }

    /// <summary>
    ///     Gets the name of a property based on a string.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="name"></param>
    /// <returns></returns>
    internal T Get<T>(string name)
    {
        object value = null;
        if (_properties.TryGetValue(name, out value))
            return value == null ? default(T) : (T) value;
        return default(T);
    }

    #endregion

    #region Setters

    /// <summary>
    ///     Sets the value of a property whilst automatically looking up its caller name.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="value"></param>
    internal void Set<T>(T value)
    {
        var propertyName = stackTrace.GetFrame(1).GetMethod().Name.Substring(4); // strips the set_ from name;
        Set(value, propertyName);
    }

    /// <summary>
    ///     Sets the value of a property
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="value"></param>
    /// <param name="name"></param>
    internal void Set<T>(T value, string propertyName)
    {
        Debug.Assert(propertyName != null, "name != null");
        if (Equals(value, Get<T>(propertyName)))
            return;
        _properties[propertyName] = value;
        NotifyOfPropertyChange(propertyName);
    }

    /// <summary>
    ///     Sets the value of a property based off an Expression (()=>FieldName)
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="value"></param>
    /// <param name="property"></param>
    internal void Set<T>(T value, Expression<Func<T>> property)
    {
        var propertyName = GetPropertyName(property);

        Debug.Assert(propertyName != null, "name != null");

        if (Equals(value, Get<T>(propertyName)))
            return;
        _properties[propertyName] = value;
        NotifyOfPropertyChange(propertyName);
    }

    #endregion
}

এই কোডটি আপনাকে এই জাতীয় সম্পত্তি সমর্থন ক্ষেত্রগুলি লিখতে সক্ষম করে:

  public string Text
    {
        get { return Get<string>(); }
        set { Set(value); }
    }

তদুপরি, পুনঃভাগে আপনি যদি কোনও প্যাটার্ন / অনুসন্ধান স্নিপেট তৈরি করেন তবে সাধারণ প্রপ ক্ষেত্রগুলিকে উপরের ব্যাকিংয়ে রূপান্তরিত করে আপনি নিজের কর্মপ্রবাহটি স্বয়ংক্রিয় করতে পারবেন।

প্যাটার্ন অনুসন্ধান:

public $type$ $fname$ { get; set; }

প্যাটার্ন প্রতিস্থাপন:

public $type$ $fname$
{
    get { return Get<$type$>(); }
    set { Set(value); }
}

2

আমি একটি নিবন্ধ লিখেছি যা এটির সাথে সহায়তা করে ( https://msdn.microsoft.com/magazine/mt736453 )। আপনি সলসফট.ডাটাবাইন্ডিং নিউগেট প্যাকেজটি ব্যবহার করতে পারেন। তারপরে আপনি এই জাতীয় কোড লিখতে পারেন:

public class TestViewModel : IRaisePropertyChanged
{
  public TestViewModel()
  {
    this.m_nameProperty = new NotifyProperty<string>(this, nameof(Name), null);
  }

  private readonly NotifyProperty<string> m_nameProperty;
  public string Name
  {
    get
    {
      return m_nameProperty.Value;
    }
    set
    {
      m_nameProperty.SetValue(value);
    }
  }

  // Plus implement IRaisePropertyChanged (or extend BaseViewModel)
}

উপকারিতা:

  1. বেস ক্লাস .চ্ছিক
  2. প্রতি 'সেট মান' এর প্রতিফলন নেই
  3. অন্যান্য বৈশিষ্ট্যের উপর নির্ভর করে এমন বৈশিষ্ট্য থাকতে পারে এবং সেগুলি যথাযথ ইভেন্টগুলি স্বয়ংক্রিয়ভাবে উত্থাপন করে (নিবন্ধটির একটি উদাহরণ রয়েছে)

2

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

সমস্যাটি হল আপনি কোনও সম্পত্তি উল্লেখ করতে পারবেন না। তবে আপনি সেই সম্পত্তিটি সেট করতে কোনও অ্যাকশন ব্যবহার করতে পারেন।

protected bool TrySetProperty<T>(Action<T> property, T newValue, T oldValue, [CallerMemberName] string propertyName = null)
{
    if (EqualityComparer<T>.Default.Equals(oldValue, newValue))
    {
        return false;
    }

    property(newValue);
    RaisePropertyChanged(propertyName);
    return true;
}

এটি নিম্নলিখিত কোড এক্সট্র্যাক্টের মতো ব্যবহার করা যেতে পারে।

public int Prop {
    get => model.Prop;
    set => TrySetProperty(x => model.Prop = x, value, model.Prop);
}

লিনকিউ ব্যবহার করে এমন একটি পদ্ধতি এবং প্রতিবিম্ব ব্যবহার করে এমন একটি পদ্ধতি সহ পদ্ধতির সম্পূর্ণ বাস্তবায়ন এবং একই ফলাফল অর্জনের কয়েকটি বিভিন্ন উপায়ের জন্য এই বিটবকেট রেপোটি দেখুন । মনে রাখবেন যে এই পদ্ধতিগুলি ধীর গতির কর্মক্ষম।


1

এই ধরণের প্রপার্টি প্রয়োগ করার সময় আপনি অন্যান্য বিষয়গুলি বিবেচনা করতে চাইতে পারেন এটি হ'ল INOTIFPropertyChang * ed * ing উভয় ইভেন্ট আর্গুমেন্ট ক্লাস ব্যবহার করে।

যদি আপনার প্রচুর সংখ্যক বৈশিষ্ট্য সেট করা থাকে তবে ইভেন্ট আর্গুমেন্ট শ্রেণীর উদাহরণগুলির সংখ্যা বিশাল হতে পারে, সেগুলি ক্যাশে বিবেচনা করা উচিত কারণ সেগুলির মধ্যে একটি স্ট্রিং বিস্ফোরণ ঘটতে পারে one

এই বাস্তবায়ন এবং এটি কেন কল্পনা করা হয়েছিল তার ব্যাখ্যাটি দেখুন।

জোশ স্মিথস ব্লগ


1

আমি সক্রিয় সন্ধান পেয়েছি - অটোমেটিক INotifyPropertyChanged , আমার এখনও এটি ব্যবহার করা হয়নি, তবে এটি দেখতে দুর্দান্ত দেখাচ্ছে।

এটির ওয়েবসাইট থেকে উদ্ধৃতি দিতে ...


স্ট্রিং হিসাবে সম্পত্তি নাম উল্লেখ না করে সম্পত্তি পরিবর্তন বিজ্ঞপ্তি প্রেরণ করুন।

পরিবর্তে, এই জাতীয় বৈশিষ্ট্য লিখুন:

public int Foo
{
    get { return _foo; }
    set { SetValue(ref _foo, value); }  // <-- no property name here
}

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


1

প্রতিবিম্ব ব্যবহার করে একটি ধারণা:

class ViewModelBase : INotifyPropertyChanged {

    public event PropertyChangedEventHandler PropertyChanged;

    bool Notify<T>(MethodBase mb, ref T oldValue, T newValue) {

        // Get Name of Property
        string name = mb.Name.Substring(4);

        // Detect Change
        bool changed = EqualityComparer<T>.Default.Equals(oldValue, newValue);

        // Return if no change
        if (!changed) return false;

        // Update value
        oldValue = newValue;

        // Raise Event
        if (PropertyChanged != null) {
            PropertyChanged(this, new PropertyChangedEventArgs(name));
        }//if

        // Notify caller of change
        return true;

    }//method

    string name;

    public string Name {
        get { return name; }
        set {
            Notify(MethodInfo.GetCurrentMethod(), ref this.name, value);
        }
    }//method

}//class

এটি বেশ দুর্দান্ত, আমি এটি এক্সপ্রেশন পদ্ধতির চেয়ে বেশি পছন্দ করি। নেতিবাচক দিক থেকে, ধীর হওয়া উচিত।
নওফাল

1

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

public abstract class AbstractObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    protected virtual bool SetValue<TKind>(ref TKind Source, TKind NewValue, params string[] Notify)
    {
        //Set value if the new value is different from the old
        if (!Source.Equals(NewValue))
        {
            Source = NewValue;

            //Notify all applicable properties
            foreach (var i in Notify)
                OnPropertyChanged(i);

            return true;
        }

        return false;
    }

    public AbstractObject()
    {
    }
}

অন্য কথায়, উপরোক্ত সমাধানটি সুবিধাজনক যদি আপনি এটি করতে কিছু মনে করেন না:

public class SomeObject : AbstractObject
{
    public string AnotherProperty
    {
        get
        {
            return someProperty ? "Car" : "Plane";
        }
    }

    bool someProperty = false;
    public bool SomeProperty
    {
        get
        {
            return someProperty;
        }
        set
        {
            SetValue(ref someProperty, value, "SomeProperty", "AnotherProperty");
        }
    }

    public SomeObject() : base()
    {
    }
}

পেশাদাররা

  • প্রতিবিম্ব নেই
  • পুরানো মান! = নতুন মান হলে কেবল তা জানায়
  • এক সাথে একাধিক বৈশিষ্ট্য অবহিত করুন

কনস

  • কোনও অটো বৈশিষ্ট্য নেই (যদিও আপনি উভয়ের জন্য সমর্থন যোগ করতে পারেন!)
  • কিছু শব্দভাবাপন্নতা
  • বক্সিং (ছোট পারফরম্যান্স হিট?)

হায়, এটি করার চেয়ে এটি আরও ভাল,

set
{
    if (!someProperty.Equals(value))
    {
        someProperty = value;
        OnPropertyChanged("SomeProperty");
        OnPropertyChanged("AnotherProperty");
    }
}

প্রতিটি একক সম্পত্তির জন্য, যা অতিরিক্ত ভার্বোসিসহ একটি দুঃস্বপ্ন হয়ে ওঠে ;-(

দ্রষ্টব্য, আমি দাবি করি না যে এই সমাধানটি অন্যদের তুলনায় আরও ভাল পারফরম্যান্স-ভিত্তিক, যারা উপস্থাপিত অন্যান্য সমাধানগুলি পছন্দ করেন না তাদের পক্ষে এটি একটি কার্যকর সমাধান।


1

আমি পর্যবেক্ষণযোগ্য প্যাটার্নটি বাস্তবায়নের জন্য এই বেস ক্লাসটি নিয়ে এসেছি, আপনার যা প্রয়োজন (তা "স্বয়ংক্রিয়ভাবে" সেটটি বাস্তবায়ন করে নিন) বেশিরভাগই করে । প্রোটোটাইপ হিসাবে আমি এটিতে এক ঘন্টা লাইন কাটিয়েছি, সুতরাং এটির অনেক ইউনিট পরীক্ষা নেই, তবে ধারণাটি প্রমাণ করে। নোট করুন এটি Dictionary<string, ObservablePropertyContext>ব্যক্তিগত ক্ষেত্রের প্রয়োজনীয়তা অপসারণ করতে ব্যবহার করে ।

  public class ObservableByTracking<T> : IObservable<T>
  {
    private readonly Dictionary<string, ObservablePropertyContext> _expando;
    private bool _isDirty;

    public ObservableByTracking()
    {
      _expando = new Dictionary<string, ObservablePropertyContext>();

      var properties = this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).ToList();
      foreach (var property in properties)
      {
        var valueContext = new ObservablePropertyContext(property.Name, property.PropertyType)
        {
          Value = GetDefault(property.PropertyType)
        };

        _expando[BuildKey(valueContext)] = valueContext;
      }
    }

    protected void SetValue<T>(Expression<Func<T>> expression, T value)
    {
      var keyContext = GetKeyContext(expression);
      var key = BuildKey(keyContext.PropertyName, keyContext.PropertyType);

      if (!_expando.ContainsKey(key))
      {
        throw new Exception($"Object doesn't contain {keyContext.PropertyName} property.");
      }

      var originalValue = (T)_expando[key].Value;
      if (EqualityComparer<T>.Default.Equals(originalValue, value))
      {
        return;
      }

      _expando[key].Value = value;
      _isDirty = true;
    }

    protected T GetValue<T>(Expression<Func<T>> expression)
    {
      var keyContext = GetKeyContext(expression);
      var key = BuildKey(keyContext.PropertyName, keyContext.PropertyType);

      if (!_expando.ContainsKey(key))
      {
        throw new Exception($"Object doesn't contain {keyContext.PropertyName} property.");
      }

      var value = _expando[key].Value;
      return (T)value;
    }

    private KeyContext GetKeyContext<T>(Expression<Func<T>> expression)
    {
      var castedExpression = expression.Body as MemberExpression;
      if (castedExpression == null)
      {
        throw new Exception($"Invalid expression.");
      }

      var parameterName = castedExpression.Member.Name;

      var propertyInfo = castedExpression.Member as PropertyInfo;
      if (propertyInfo == null)
      {
        throw new Exception($"Invalid expression.");
      }

      return new KeyContext {PropertyType = propertyInfo.PropertyType, PropertyName = parameterName};
    }

    private static string BuildKey(ObservablePropertyContext observablePropertyContext)
    {
      return $"{observablePropertyContext.Type.Name}.{observablePropertyContext.Name}";
    }

    private static string BuildKey(string parameterName, Type type)
    {
      return $"{type.Name}.{parameterName}";
    }

    private static object GetDefault(Type type)
    {
      if (type.IsValueType)
      {
        return Activator.CreateInstance(type);
      }
      return null;
    }

    public bool IsDirty()
    {
      return _isDirty;
    }

    public void SetPristine()
    {
      _isDirty = false;
    }

    private class KeyContext
    {
      public string PropertyName { get; set; }
      public Type PropertyType { get; set; }
    }
  }

  public interface IObservable<T>
  {
    bool IsDirty();
    void SetPristine();
  }

এখানে ব্যবহার

public class ObservableByTrackingTestClass : ObservableByTracking<ObservableByTrackingTestClass>
  {
    public ObservableByTrackingTestClass()
    {
      StringList = new List<string>();
      StringIList = new List<string>();
      NestedCollection = new List<ObservableByTrackingTestClass>();
    }

    public IEnumerable<string> StringList
    {
      get { return GetValue(() => StringList); }
      set { SetValue(() => StringIList, value); }
    }

    public IList<string> StringIList
    {
      get { return GetValue(() => StringIList); }
      set { SetValue(() => StringIList, value); }
    }

    public int IntProperty
    {
      get { return GetValue(() => IntProperty); }
      set { SetValue(() => IntProperty, value); }
    }

    public ObservableByTrackingTestClass NestedChild
    {
      get { return GetValue(() => NestedChild); }
      set { SetValue(() => NestedChild, value); }
    }

    public IList<ObservableByTrackingTestClass> NestedCollection
    {
      get { return GetValue(() => NestedCollection); }
      set { SetValue(() => NestedCollection, value); }
    }

    public string StringProperty
    {
      get { return GetValue(() => StringProperty); }
      set { SetValue(() => StringProperty, value); }
    }
  }

1

আমি ReactiveProperty ব্যবহার করার পরামর্শ দিই। এটি ফডি বাদে সংক্ষিপ্ততম পদ্ধতি।

public class Data : INotifyPropertyChanged
{
    // boiler-plate
    ...
    // props
    private string name;
    public string Name
    {
        get { return name; }
        set { SetField(ref name, value, "Name"); }
    }
}

পরিবর্তে

public class Data
{
    // Don't need boiler-plate and INotifyPropertyChanged

    // props
    public ReactiveProperty<string> Name { get; } = new ReactiveProperty<string>();
}

( ডকস )


0

অন্য আইডিয়া ...

 public class ViewModelBase : INotifyPropertyChanged
{
    private Dictionary<string, object> _propertyStore = new Dictionary<string, object>();
    protected virtual void SetValue<T>(T value, [CallerMemberName] string propertyName="") {
        _propertyStore[propertyName] = value;
        OnPropertyChanged(propertyName);
    }
    protected virtual T GetValue<T>([CallerMemberName] string propertyName = "")
    {
        object ret;
        if (_propertyStore.TryGetValue(propertyName, out ret))
        {
            return (T)ret;
        }
        else
        {
            return default(T);
        }
    }

    //Usage
    //public string SomeProperty {
    //    get { return GetValue<string>();  }
    //    set { SetValue(value); }
    //}

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        var temp = PropertyChanged;
        if (temp != null)
            temp.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

0

=> নিম্নলিখিত বৈশিষ্ট্য সহ এখানে আমার সমাধান

 public ResourceStatus Status
 {
     get { return _status; }
     set
     {
         _status = value;
         Notify(Npcea.Status,Npcea.Comments);
     }
 }
  1. কোন রিফলেকশন
  2. সংক্ষিপ্ত স্বরলিপি
  3. আপনার ব্যবসায়ের কোডে কোনও যাদু স্ট্রিং নেই
  4. অ্যাপ্লিকেশন জুড়ে প্রপার্টি চেঞ্জড এভেন্টআর্গের পুনরায় ব্যবহারযোগ্যতা
  5. এক বিবৃতিতে একাধিক বৈশিষ্ট্য অবহিত করার সম্ভাবনা

0

এটা ব্যবহার কর

using System;
using System.ComponentModel;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;


public static class ObservableFactory
{
    public static T Create<T>(T target)
    {
        if (!typeof(T).IsInterface)
            throw new ArgumentException("Target should be an interface", "target");

        var proxy = new Observable<T>(target);
        return (T)proxy.GetTransparentProxy();
    }
}

internal class Observable<T> : RealProxy, INotifyPropertyChanged, INotifyPropertyChanging
{
    private readonly T target;

    internal Observable(T target)
        : base(ImplementINotify(typeof(T)))
    {
        this.target = target;
    }

    public override IMessage Invoke(IMessage msg)
    {
        var methodCall = msg as IMethodCallMessage;

        if (methodCall != null)
        {
            return HandleMethodCall(methodCall);
        }

        return null;
    }

    public event PropertyChangingEventHandler PropertyChanging;
    public event PropertyChangedEventHandler PropertyChanged;



    IMessage HandleMethodCall(IMethodCallMessage methodCall)
    {
        var isPropertySetterCall = methodCall.MethodName.StartsWith("set_");
        var propertyName = isPropertySetterCall ? methodCall.MethodName.Substring(4) : null;

        if (isPropertySetterCall)
        {
            OnPropertyChanging(propertyName);
        }

        try
        {
            object methodCalltarget = target;

            if (methodCall.MethodName == "add_PropertyChanged" || methodCall.MethodName == "remove_PropertyChanged"||
                methodCall.MethodName == "add_PropertyChanging" || methodCall.MethodName == "remove_PropertyChanging")
            {
                methodCalltarget = this;
            }

            var result = methodCall.MethodBase.Invoke(methodCalltarget, methodCall.InArgs);

            if (isPropertySetterCall)
            {
                OnPropertyChanged(methodCall.MethodName.Substring(4));
            }

            return new ReturnMessage(result, null, 0, methodCall.LogicalCallContext, methodCall);
        }
        catch (TargetInvocationException invocationException)
        {
            var exception = invocationException.InnerException;
            return new ReturnMessage(exception, methodCall);
        }
    }

    protected virtual void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }

    protected virtual void OnPropertyChanging(string propertyName)
    {
        var handler = PropertyChanging;
        if (handler != null) handler(this, new PropertyChangingEventArgs(propertyName));
    }

    public static Type ImplementINotify(Type objectType)
    {
        var tempAssemblyName = new AssemblyName(Guid.NewGuid().ToString());

        var dynamicAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(
            tempAssemblyName, AssemblyBuilderAccess.RunAndCollect);

        var moduleBuilder = dynamicAssembly.DefineDynamicModule(
            tempAssemblyName.Name,
            tempAssemblyName + ".dll");

        var typeBuilder = moduleBuilder.DefineType(
            objectType.FullName, TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract);

        typeBuilder.AddInterfaceImplementation(objectType);
        typeBuilder.AddInterfaceImplementation(typeof(INotifyPropertyChanged));
        typeBuilder.AddInterfaceImplementation(typeof(INotifyPropertyChanging));
        var newType = typeBuilder.CreateType();
        return newType;
    }
}

}

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