none
List<T> 共用Event事件 RRS feed

  • 問題

  • 各位前輩好

    小弟目前遇到的問題是這樣的 我寫了一個Class物件,暫時命名為 Device

    裡面宣告了一個 String DeviceVal 以及一個DataChange的事件

    在Val 被修改的時候,觸發這個Event事件

    //Class 大致是這樣
    public Class Device{
        string devicename = "";
        public delegate void ErrorDelegate(string sVal);
        public event ErrorDelegate DataChange;
    
        private string _sVal = "";
        public string sVal {
            get { return _sVal; }
            set {
                DataChange(value);
                _sVal = value;
            }
        }
    }


    在外部建立物件的時候,如果是正常宣告建立沒什麼太大問題

    Device device = new Device();
    device.DataChange += device_Datachange();
    private void device_Datachange(Sval){
        Console.WriteLine(DeviceVal 被修改);
    }

    現在如果我要宣告50個相同物件,應該不是一樣的程式碼複製50次吧..

    所以我想到的方式是透過List<T> 加上迴圈去增加

    List<Device> DeviceList = new List<Device>();

    for (int i = 0; i < 50; i++) {
    DeviceList.Add(
    new Device {
    devicename = "device_"+i
    }
    );
    }

    foreach (var deviceitem in DeviceList) {
    deviceitem.DataChange += device_Datachange;
    }


    private void device_Datachange(Sval){
        Console.WriteLine(DeviceVal 被修改);
    }


    這樣建立方式基本上沒有問題,我修改值的時候Event也會確實觸發

    但是!!

    我要怎麼知道是第幾個List<Device>物件的值被改變所觸發這個Event事件

    不知道前輩們是怎麼解決這樣的問題,希望可以給我一些參考資源

    PS:在不修改物件前提有辦法實現嗎,因為有些物件已經被打包成Dll了




    • 已編輯 open852134 2017年3月3日 上午 06:15
    2017年3月3日 上午 06:07

解答

  • 加一個 object sender 在你的 delegate之中,

    public delegate void ErrorDelegate(object sender, string sVal);

    set 時就傳 this , 

    DataChange(this, value);

    如下,

    public class Device
    {
        string devicename = "";
        public delegate void ErrorDelegate(object sender, string sVal);
        public event ErrorDelegate DataChange;
    
        private string _sVal = "";
        public string sVal
        {
            get { return _sVal; }
            set
            {
                DataChange(this, value);
                _sVal = value;
            }
        }
    
        public static List<Device> TestDevice()
        {
            List<Device> DeviceList = new List<Device>();
            for (int i = 0; i < 50; i++)
            {
                DeviceList.Add(
                    new Device
                    {
                        devicename = "device_" + i
                    }
                );
            }
    
            foreach (var deviceitem in DeviceList)
            {
                deviceitem.DataChange += device_Datachange;
            }
            return DeviceList;
        }
    
        private static void device_Datachange(object sender, string val)
        {
            Console.WriteLine($"{(sender as Device).devicename} 's {val}" );
        }
    }


    • 已編輯 亂馬客 2017年3月3日 上午 10:25
    • 已標示為解答 open852134 2017年3月3日 下午 02:25
    2017年3月3日 上午 10:23

所有回覆

  • 您好,

    您的event 可以多加一個 sender 參數,來表示引發 event 的那個物件。

    2017年3月3日 上午 06:14
  • 前輩你好

    請問sender參數 是要加在我Class建立delegate的時候嗎

    還是在在宣告建立物件後  device.Datachange += device_Datachange;

    這邊也可以加入sender

    2017年3月3日 上午 06:18
  • 加一個 object sender 在你的 delegate之中,

    public delegate void ErrorDelegate(object sender, string sVal);

    set 時就傳 this , 

    DataChange(this, value);

    如下,

    public class Device
    {
        string devicename = "";
        public delegate void ErrorDelegate(object sender, string sVal);
        public event ErrorDelegate DataChange;
    
        private string _sVal = "";
        public string sVal
        {
            get { return _sVal; }
            set
            {
                DataChange(this, value);
                _sVal = value;
            }
        }
    
        public static List<Device> TestDevice()
        {
            List<Device> DeviceList = new List<Device>();
            for (int i = 0; i < 50; i++)
            {
                DeviceList.Add(
                    new Device
                    {
                        devicename = "device_" + i
                    }
                );
            }
    
            foreach (var deviceitem in DeviceList)
            {
                deviceitem.DataChange += device_Datachange;
            }
            return DeviceList;
        }
    
        private static void device_Datachange(object sender, string val)
        {
            Console.WriteLine($"{(sender as Device).devicename} 's {val}" );
        }
    }


    • 已編輯 亂馬客 2017年3月3日 上午 10:25
    • 已標示為解答 open852134 2017年3月3日 下午 02:25
    2017年3月3日 上午 10:23
  • 要讓你的程式碼看起來更有個樣子,直接實作INotifyPropertyChanged 介面 就好了,漂亮又好用,比自己搞個 delegate 還來得更能表現出 "屬性變更" 這個意圖。

    他的第二個參數可以把變更的屬性名稱傳出來,如果你的 Device 有更多屬性要做這件事,還可以共用一個事件,是不是更棒 ?

    臨時寫個 sample,裡面故意給兩個屬性都會做變更通知,直接用 Console 範本,將就著點看吧。

    註: 記得先 using System.ComponentModel; 才不會寫的落落長。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.ComponentModel;
    
    namespace ConsoleApplication8
    {
    
        class Program
        {
    
            static void Main(string[] args)
            {
                var devices = new List<Device>
                {
                    new Device {Name="A" },
                    new Device {Name="B" }
                };
    
                foreach (var device in devices)
                {
                    device.PropertyChanged += Device_PropertyChanged;
                }
    
                devices[1].Val = 100;
    
                Console.ReadLine();
            }
    
            private static void Device_PropertyChanged(object sender, PropertyChangedEventArgs e)
            {
                var name = ((Device)sender).Name;
                Console.WriteLine($" Device Name= {name} 發生變化的屬性是 {e.PropertyName }");
            }
        }
    
        public class Device : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
    
            private void OnPropertyChanged(string propertyName)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                }
            }
    
            private int _val;
            public int Val
            {
                get { return _val; }
                set
                {
                    if (_val != value)
                    {
                        _val = value;
                        OnPropertyChanged(nameof(Val));
                    }
                }
            }
    
            private int _otherVal;
            public int OtherVal
            {
                get { return _otherVal; }
                set
                {
                    if (_otherVal != value)
                    {
                        _otherVal = value;
                        OnPropertyChanged(nameof(OtherVal));
                    }
                }
            }
    
            //這個不做變更通知, 只是為了讓你測試好可以有個名字辨認而已
            public string Name { get; set; }
        }
    }
    


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    2017年3月3日 下午 01:41
    版主
  • 感謝兩位前輩的回應!!

    所以在建立物件的時候就必須要加入Object這個項目

    我後來測試也是像亂馬客前輩提供的方式,去修改我的Class物件

    也感謝版主提供另一個方式來解決我的需求!!

    所以如果我在建立物件Event事件沒有提供object的話

    又透過迴圈這種方式建立,是無法得知由哪個物件所觸發的

    不過這也是合理啦 Form的Event事件也就都有帶object的屬性

    不然什麼都沒帶怎麼知道那些物件有使用這個處理function

    2017年3月3日 下午 02:25
  • .Net 內建控制項都會有個 Tag 可以註記,一般也可以拿這個來放索引值。

    你可以模擬這種做法。


    不精確的問法,就會得到隨便猜的答案;自己都不肯花時間好好描述問題,又何必期望網友會認真回答?

    2017年3月3日 下午 04:50