免費、免安裝、找車位就是快!超好用 Parking Go 找車位使用教學攻略_台中搬家

台中搬家公司費用怎麼算?

擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司

為大家介紹一個超好用的免費尋找停車位服務「 Parking Go 找車位 」,很多人家裡都有車,不管是平日通勤、接送小孩、買東西或是假日出遊時都可能會需要開車,但開車不是問題,比較大的問題是「堵車」;更要命的問題是「停車」,有時候好不容易抵達目的地,結果繞了半天到處找不到車位的時候真的會很讓人懷疑人生。今天就來分享一個找車位 LINE 官方帳號「Parking Go」,不但讓駕駛能夠即時查詢附近是否有空停車位,還能導航帶你立刻去搶位置,減少繞圈找車位浪費生命的困擾,還可以節省車輛油耗,同時兼顧環保,最最重要的是它不但免費,甚至連 App 都不需要安裝,只要用 LINE 開啟就能使用!

免費、免安裝!超好用 Parking Go 找車位使用教學攻略

阿達也做了Parking Go找車位的實測影片,大家可以看一下,真的蠻厲害的(歡迎訂閱電腦王阿達頻道):

 

Parking Go 使用超級簡單,連 App 都不用裝,只要開啟您的 LINE App,接著用搜尋功能輸入「@parking-go」,在官方帳號的選項中就可以看到「Parking Go找車位」(或用手機點我也可以),加入好友後記得要許可 LINE 的存取權,不然會不能使用:

接著就是同意使用協定與輸入個人資料與車牌號碼,如果有活動有機會抽獎:

註冊完成後就可以進入 Parking Go 主選單,其中第一個是操作流程介紹,裡面也有一些抽獎活動資訊,另外同時也有停車大聲公的下載(這個服務的基底就是我們之前介紹過的停車大聲公,不過現在更結合了物聯網資訊,變的更強大了):

第一次使用時,系統會要求使用你目前的位置(GPS權限),記得要給它。接著按下畫面上的「定位」就會顯示使用者目前的位置:

 

Parking Go 找車位實戰

舉例來說,如果你想要找附近哪裡有停車位的話,只要將車停路邊或請副駕友人幫你按下「附近停車位」功能,如此系統就會自動幫你找出所在地附近目前還有位置的停車格,相當簡單:

或者是您想要找等下要去的目的地附近還有沒有停車格,只要輸入景點名稱再按下「GO」:

系統就會顯示該景點附近所以停車格與停車格編號,先不要看到這麼多停車格就開心嘿,放大顯示區域的話就會看到綠點與紅點顯示,那代表的是該停車格上面有沒有車輛停在上面,以這個畫面來說只剩下一個位置:

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

台中搬家公司推薦超過30年經驗,首選台中大展搬家

為了不要浪費你的時間,可以直接開啟下方的「顯示可停車位」,這樣系統就會排除掉已經被佔用的停車格了,您只要選有綠點的車格就好:

當選定打算前往停車的位置後,點擊綠點就會顯示停車格位置、類型與費率,點擊車格詳細資訊就可以切換到 Google 地圖導航過去:

Parking Go 功能驗證

接著為了取材方便,阿達找了台南大學附近的停車格去現地看看 Parking Go 是不是真的那麼厲害,以下圖為例,除了顯示哪些停車格被佔用;哪些還有位置,甚至還有區分身心障礙者專用停車格的區別:

到達現地,號碼一致,前面也有身心障礙停車格,真的很厲害:

主要的原因就是遠傳電信與台南市政府合作,透過物聯網技術在台南市區設置了數千個「地磁偵測器」,主要分布於台南東區南紡購物中心、商業熱區、大東夜市人潮鬧區,以及台南中西區美術館、赤崁樓、孔廟、大天后宮等各級古蹟、保安路國華街美食商圈一帶,並透過 NB-IoT 物聯網技術,將偵測到的停車格車輛進出狀態回傳到主機,用路人以「Parking GO」Line官方帳號、「台南好停」App都能查詢即時停車格狀態:

其實就我自己觀察,除了路邊停車格以外,台南市區很多公有停車場也都設置了地磁偵測器,幾乎主要路段的路邊停車格都有地磁偵測器,不過還沒有百分之百上線就是了,有些地區我看到已經裝設但 Parking Go 卻還沒顯示,蠻期待這個服務在台南市全域與其他縣市都能普及,這樣大家開車就更方便了:

除了台南,遠傳也在台中等其他縣市設置上萬個「地磁偵測器」,皆可透過遠傳「Parking GO」Line官方帳號查詢,目前會員超過 11 萬人,查詢服務超過 387 萬人次。認真說希望全台灣如果所有停車格都設置的話那大家應該就能脫離整天繞路找車位的惡夢了。

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

台中搬家公司推薦超過30年經驗,首選台中大展搬家

最低只需12萬起 五款入門小鋼炮怎麼選?_台中搬家

台中搬家公司費用怎麼算?

擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司

雷諾在性能上的摸索研究,一點都不比別的車廠差。梅甘娜RS也是一輛在車迷圈子內名聲頗大的鋼炮。就因為它曾在紐北創造了前驅車最速紀錄,這就已經為梅甘娜RS蒙上一層神聖的面紗。不過這台車又是叫好不叫座,據聞在國內能以低於30萬的價格收到一台。

不少車迷心裏都會有一個鋼炮夢,他們喜歡的這些鋼炮車型,兩廂的車身造型緊湊得像顆子彈,又能提供比較出色的駕控體驗。今天就來給大家聊聊五台比較有代表性,又能買得到的鋼炮車型。

很意外,雨燕居然上榜了。這台在國內曾經滿大街跑小兩廂,嚴格來說還不算鋼炮。但自從聽說了以前的高中同學花了兩萬淘了輛二手雨燕,換了個排氣管后就搖着棍棍去跑山了,我這才意識到這小傢伙是多麼的充滿樂趣。

然而這台雨燕Sport是台進口車…售價要13萬左右,想想還是來台二手的比較實際。

福特造的小車一向口碑不錯,就比如這台N年沒改過款的兩廂小鋼炮嘉年華ST。1.6T發動機帶着180馬力的輸出送你上路,加上很鍛煉人意志力的六擋手動變速箱,足以讓你整天把駕駛樂趣掛在嘴邊了。

買這台車之前,

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

台中搬家公司推薦超過30年經驗,首選台中大展搬家

可能你得想好一個理由來說服家人為什麼買一台起步價要18萬的嘉年華..

雷諾在性能上的摸索研究,一點都不比別的車廠差。梅甘娜RS也是一輛在車迷圈子內名聲頗大的鋼炮。就因為它曾在紐北創造了前驅車最速紀錄,這就已經為梅甘娜RS蒙上一層神聖的面紗。

不過這台車又是叫好不叫座,據聞在國內能以低於30萬的價格收到一台。

福特的鋼炮小車子,今年的代表作必定是這台藍得發紫的福克斯RS了。而它也具備了叫板像寶馬M2、思域Type R這類車型的實力。加上一鍵飄移模式,簡直就是把遊戲機搬到了這台車上。對於粉絲來說,得到它就像得到了全世界。

同樣的,買這輛車的盆友可能也要考慮一下向家人解釋為什麼花40萬來買輛福克斯了..

最後一台車,是奔馳的A45 AMG。這台純正的AMG車型,我們才在前陣子試駕過,確實是一輛剛猛的小鋼炮,夠鋼,也能打炮(說的是排氣管放炮聲)。高達380馬力的動力輸出,已經能在天朝當路霸了。

16款的A45 AMG起售價59萬,加個十萬左右就能買最新的保時捷718 Boxster了,這樣一想,還真是讓人糾結啊。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

台中搬家公司費用怎麼算?

擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司

蒙古爆發鼠疫 傳已死2人封城 外國遊客禁離境_台中搬家

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

台中搬家公司推薦超過30年經驗,首選台中大展搬家

摘錄自2019年5月5日自由時報報導

蒙古國傳爆發鼠疫,其中傳出鼠疫的烏列蓋市已有2人死亡,全城封閉檢疫,以致在當地旅遊外國遊客全被禁止回國。

《俄羅斯通訊社》報導,最初傳出爆發鼠疫訊息的是一個叫 vesti.ru網站,接著,俄羅斯駐蒙古大使館證實這個訊息。

台中搬家公司費用怎麼算?

擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司

綜合媒體報導,這場瘟疫初步認定是由當地土撥鼠傳播,已有2人死亡,其中,有人在醫院內死亡,以致在4月30日至5月1日夜裡,爆發鼠疫的烏列蓋市全城封閉檢疫,有外國遊客恐慌想要離城返國,即被禁止,也因此外傳鼠疫爆發事件;蒙古國宣布,檢疫將到今天(5月5日),是否繼續封城檢疫,還沒有進一步消息。

西伯利亞媒體也指出,蒙古人有生吃老鼠腎臟的傳統,認為會健康,每年都會發生鼠疫,並非新的疾病。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

台中搬家公司推薦超過30年經驗,首選台中大展搬家

C# 根據BackgroundWorker異步模型和ProgressBar控件,自定義進度條控件_台中搬家

台中搬家公司費用怎麼算?

擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司

前言

程序開發過程中,難免會有的業務邏輯,或者算法之類產生讓人能夠感知的耗時操作,例如循環中對複雜邏輯處理;獲取數據庫百萬乃至千萬級數據;http請求的時候等……
用戶在使用UI操作並不知道程序的內部處理,從而誤操作導致程序無響應,關閉程序等待影響體驗的情況,因此,在等待過程中提供友好的等待提示是有必要的,接下來
我們一起封裝一個自定義進度條控件!

主要使用技術(C#相關)

  • BackgroundWoker異步模型
  • ProgressBar控件
  • 泛型
  • 定時器 System.Timers.Timer

自定義控件開發

項目解決方案

  • BackgroundworkerEx : 自定義進度條控件工程
  • Test : 調用BackgroundworkerEx的工程(只是展示如何調用)

處理控件樣式

  • 新建一個ProgressbarEx名稱的 用戶控件

  • 添加Labal控件(lblTips),用於展示進度條显示的信息狀態

  • 添加一個PictureBox控件(PicStop),充當關閉按鈕,用於獲取用戶點擊事件,觸發關閉/終止進度條

  • 添加進度條ProgressBar控件(MainProgressBar)

  • 處理代碼如下:

  1. 進度條樣式為”不斷循環”,並且速度為50
  2. 該自定義用戶控件不展示在任務欄中
  3. 圖片控件被點擊事件——>設置當前屬性IsStop=true,指示過程終止;
  4. TipMessage屬性,用於設置進度條的信息
  5. SetProgressValue(int value) 設置進度條的Value屬性,使得在ProgressBarStyle.Marquee樣式中動畫平滑
  6. MouseDown/MouseUp/MouseMove這三個事件是用於拖動無邊框的用戶控件(代碼就不貼了)
public ProgressbarEx()
{
    InitializeComponent();

    MainProgressBar.Style = ProgressBarStyle.Marquee;
    MainProgressBar.MarqueeAnimationSpeed = 50;

    this.ShowInTaskbar = false;

    PicStop.Click += (s, eve) =>
    {
    IsStop = true;
    };

    this.MouseDown += CusProgressForm_MouseDown;
    this.MouseUp += CusProgressForm_MouseUp;
    this.MouseMove += CusProgressForm_MouseMove;
}

/// <summary>
/// Need Stop ?
/// </summary>
public bool IsStop { get; private set; } = false;

/// <summary>
/// TipMessage
/// </summary>
public string TipMessage { get; set; }

/// <summary>
/// TipMessage
/// </summary>
public string TipMessage
{
    get
    {
    return lblTips.Text;
    }
    set
    {

    lblTips.Text = value;
    }
}

/// <summary>
/// Set ProgressBar value ,which makes ProgressBar smooth
/// </summary>
/// <param name="value"></param>
public void SetProgressValue(int value)
{
    if (MainProgressBar.Value == 100) MainProgressBar.Value = 0;

    MainProgressBar.Value += value;

}

到現在,這個自定義進度條控件的樣式基本完成了.

功能邏輯處理

運行前所需

  • 定義BackgroundWorkerEx<T>泛型類,並且繼承於 IDisposable
    1. 釋放資源;
 		/// <summary>
        /// Dispose
        /// </summary>
        public void Dispose()
        {
            try
            {
                DoWork = null;
                RunWorkCompleted = null;
                WorkStoped = null;

                _mWorkerThread = null;
                _mWorker.Dispose();
                _mWorker = null;
                _mTimer = null;
            }
            catch (Exception){}
        }
  1. T用與異步處理的時候,傳遞T類型
  • 因為我們是通過.Net 的 BackgroundWorker異步模型來做的,所以我們理所當然定義相關的事件:
    1. 異步開始
    2. 異步完成
    3. 加上我們自定義擴展的異步停止
    4. ……報告進度事件在此進度條樣式中並不需要

我們先定義這四個事件所用到的參數,因為在BackgroundWorkerEx<T>泛型類中,我們還是使用BackgroundWorker來處理異步過程,因此我們定義的參數泛型類需要繼承原來的參數類型,並且在傳輸傳遞中,將原生BackgroundWorkerArgument,Result屬性轉成全局的泛型T,這樣我們在外部調用的時候,拿到的返回結果就是我們傳入到BackgroundWorkerEx<T>泛型類中的T類型,而不需要使用as進行轉換; 注:因為原生沒有停止相關事件,所以自定義異步停止的事件參數使用的是DoWorkEventArgs<T>

    public class DoWorkEventArgs<T> : DoWorkEventArgs
    {
        public new T Argument { get; set; }
        public new T Result { get; set; }
        public DoWorkEventArgs(object argument) : base(argument)
        {
            Argument = (T)argument;
        }
    }


    public class RunWorkerCompletedEventArgs<T> : RunWorkerCompletedEventArgs
    {
        public new T Result { get; set; }
        public RunWorkerCompletedEventArgs(object result, Exception error, bool cancelled) : base(result, error, cancelled)
        {
            Result = (T)result;
        }
    }

接着我們需要去定義事件,參數使用以上定義的泛型類

	public delegate void DoWorkEventHandler(DoWorkEventArgs<T> Argument);
        /// <summary>
        /// StartAsync
        /// </summary>
        public event DoWorkEventHandler DoWork;

        public delegate void StopEventHandler(DoWorkEventArgs<T> Argument);
        /// <summary>
        /// StopAsync
        /// </summary>
        public event StopEventHandler WorkStoped;

        public delegate void RunWorkCompletedEventHandler(RunWorkerCompletedEventArgs<T> Argument);
        /// <summary>
        /// FinishAsync
        /// </summary>
        public event RunWorkCompletedEventHandler RunWorkCompleted;
  • 定義全局的字段
    1. private BackgroundWorker _mWorker = null;異步操作必要;
    2. private T _mWorkArg = default(T);操作傳遞進來的參數類並且返回到外部
    3. private Timer _mTimer; 定時器檢測自定義進度條控件屬性IsStop是否為true,並且動態修改進度條消息
    4. private Thread _mWorkerThread = null;異步操作在該線程中,終止時調用About()拋出ThreadAbortException異常,用於標記當前是停止而不是完成狀態
    5. private int _miWorkerStartDateSecond = 0; 異步消耗時間(非必要)
    6. private int _miShowProgressCount = 0; 動態显示”.”的個數(非必要)
    7. private ProgressbarEx _mfrmProgressForm = null; 自定義進度條控件實例
        /// <summary>
        /// .Net  BackgroundWorker
        /// </summary>
        private BackgroundWorker _mWorker = null;

        /// <summary>
        /// Whole Para
        /// </summary>
        private T _mWorkArg = default(T);

        /// <summary>
        /// Timer
        /// </summary>
        private Timer _mTimer = null;

        /// <summary>
        /// WorkingThread
        /// </summary>
        private Thread _mWorkerThread = null;

        /// <summary>
        /// Async time sec
        /// </summary>
        private int _miWorkerStartDateSecond = 0;

        /// <summary>
        /// Async time dot
        /// </summary>
        private int _miShowProgressCount = 0;

        /// <summary>
        /// ProgressbarEx
        /// </summary
        private ProgressbarEx _mfrmProgressForm = null;

  • 定義全局屬性
  1. IsBusy 返回_mWorker的工作忙碌是否
  2. ProgressTip 自定義進度條控件显示內容
	/// <summary>
        /// Express Busy
        /// </summary>
        public bool IsBusy
        {
            get
            {
                if (_mWorker != null)
                {
                    return _mWorker.IsBusy;
                }
                return false;
            }
        }

        /// <summary>
        /// 進度條提示 默認: 正在加載數據,請稍後[{0}]{1}
        /// </summary>
        public string ProgressTip { get; set; } = "Elapsed Time[{0}]{1}";

**到現在,我們已經將必要的字段,屬性,樣式都處理完成!!! ** 接下來我們就要實現方法

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

台中搬家公司推薦超過30年經驗,首選台中大展搬家

方法實現

  • 異步工作事件,用法與BackgroundWorker一致,

    1. 如果調用處沒有註冊DoWork事件,則直接返回

    2. 將接受到的參數創建成泛型參數類

    3. 開線程,將異步操作放在該線程中操作,注意設置線程的IsBackground=true,防止主進程意外退出,線程還在處理

    4. 循環直到線程結束

    5. e.Result = Argument.Result;將結果賦予Result,在停止或者完成事件中可以獲取到結果

		/// <summary>
        /// Working
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Worker_DoWork(object sender, DoWorkEventArgs e)
        {
            if (DoWork == null)
            {
                e.Cancel = true;
                return;
            }

            DoWorkEventArgs<T> Argument = new DoWorkEventArgs<T>(e.Argument);

            try
            {
                if (_mWorkerThread != null && _mWorkerThread.IsAlive)
                {
                    _mWorkerThread.Abort();
                }
            }
            catch (Exception)
            {
                Thread.Sleep(50);
            }

            _mWorkerThread = new Thread(a =>
            {
                try
                {
                    DoWork?.Invoke(a as DoWorkEventArgs<T>);
                }
                catch (Exception)
                {

                }
            });

            _mWorkerThread.IsBackground = true;
            _mWorkerThread.Start(Argument);

            //Maybe cpu do not start thread
            Thread.Sleep(20);

            //Wait.....
            while (_mWorkerThread.IsAlive)
            {
                Thread.Sleep(50);
            }
            e.Result = Argument.Result;
        }
  • 異步完成/停止

    當線程停止拋出異常(catch但是不處理)/線程完成時會進入異步完成事件

    1. 完成后,將自定義進度條控件實例關閉,釋放
  1. 將全局的BackgroundWorker實例_mWorker相關事件取消註冊,並且檢查線程情況
  2. 感覺線程情況,如果線程狀態為ThreadState.Aborted意味着線程被停止了,調用停止事件,否則調用完成事件
	  /// <summary>
      /// Completed
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void Worker_RunWorkCompleted(object sender, RunWorkerCompletedEventArgs e)
      {
          try
          {
              if (_mfrmProgressForm != null)
              {
                _mfrmProgressForm.Close();
                  _mfrmProgressForm.Dispose();
                _mfrmProgressForm = null;
              }

                if (_mWorker != null)
                {
                    _mWorker.DoWork -= Worker_DoWork;
                    _mWorker.RunWorkerCompleted -= Worker_RunWorkCompleted;

                    try
                    {
                        if (_mWorkerThread != null && _mWorkerThread.IsAlive) _mWorkerThread.Abort();
                    }
                    catch (Exception) { }
                }

              //In timer, When stop progress will make thread throw AbortException
              if (_mWorkerThread != null && _mWorkerThread.ThreadState == ThreadState.Aborted)
            {
                  WorkStoped?.Invoke(new DoWorkEventArgs<T>(_mWorkArg));
              }
              else
              {
                  RunWorkCompleted?.Invoke(new RunWorkerCompletedEventArgs<T>(e.Result, e.Error, e.Cancelled));
              }
          }
          catch (Exception ex)
          {
              throw ex;
          }
      }
  • 線程開始

    1. 檢查消息提醒內容 , {0}{1}同於显示異步耗時和”..”的個數
    2. 在定時器執行方法中,檢查_mfrmProgressForm.IsStop是否為true,這個屬性標誌是否被停止;true則拋出異常
    3. _mfrmProgressForm不為Null則不斷修改當前的內容提醒,友好化,實際可以按需處理
    		  /// <summary>
            /// Timer Start 
            /// </summary>
            private void StartTimer()
            {
                //Check user ProgressTip
                if (!ProgressTip.Contains("{0}"))
                {
                    ProgressTip += "...Elapsed Time{0}{1}";
                }
    
                if (_mTimer != null) return;
    
                //On one sec 
                _mTimer = new Timer(1000);
                _mTimer.Elapsed += (s, e) =>
                {
                    //progress and it's stop flag (picture stop)||  this stop flag
                    if (_mfrmProgressForm != null && _mfrmProgressForm.IsStop)
                    {
                        if (_mWorker != null)
                        {
                            try
                            {
                                if (_mWorkerThread != null && _mWorkerThread.IsAlive)
                                {
                                    if (_mTimer != null && _mTimer.Enabled)
                                    {
                                        _mTimer.Stop();
                                        _mTimer = null;
                                    }
                                    _mWorkerThread.Abort();
                                }
                            }
                            catch (Exception) { }
                        }
                    }
    
                    if (_mfrmProgressForm != null)
                    {
                        //Callback 
                        _mfrmProgressForm.Invoke(new Action<DateTime>(elapsedtime =>
                        {
                            DateTime sTime = elapsedtime;
    
                            //worked time
                            _miWorkerStartDateSecond++;
                            if (_mfrmProgressForm != null)
                            {
                                _mfrmProgressForm.SetProgressValue(_miWorkerStartDateSecond);
                            }
    
                            //.....count
                            _miShowProgressCount++;
    
                            if (_miShowProgressCount > 6)
                            {
                                _miShowProgressCount = 1;
                            }
    
                            string[] strs = new string[_miShowProgressCount];
    
                            string ProgressStr = string.Join(".", strs);
    
                            string ProgressText = string.Format(ProgressTip, _miWorkerStartDateSecond, ProgressStr);
    
                            if (_mfrmProgressForm != null)
                            {
                                _mfrmProgressForm.TipMessage = ProgressText;
                            }
                        }), e.SignalTime);
                    }
                };
    
                if (!_mTimer.Enabled)
                {
                    _mTimer.Start();
                }
            }
    
  • 最後一步:異步開始BackgroundWorker用法一致,只是在最後開始了定時器和進度條控件而已

    /// <summary>
            /// Start AsyncWorl
            /// </summary>
            /// <param name="Para"></param>
            public void AsyncStart(T Para)
            {
                //if workeven is  null ,express user do not regist event
                if (DoWork == null)
                {
                    return;
                }
    
                _miWorkerStartDateSecond = 0;
                _miShowProgressCount = 0;
    
                //init
                if (_mWorker != null && _mWorker.IsBusy)
                {
                    _mWorker.CancelAsync();
                    _mWorker = null;
                }
    
                _mWorker = new BackgroundWorker();
    
                //create progressbar
                _mfrmProgressForm = new ProgressbarEx();
    
                //add event
                _mWorker.DoWork += Worker_DoWork;
                _mWorker.RunWorkerCompleted += Worker_RunWorkCompleted;
    
                _mWorker.WorkerReportsProgress = true;
                _mWorker.WorkerSupportsCancellation = true;
    
                //Set Whole Para
                _mWorkArg = Para;
    
                _mWorker.RunWorkerAsync(Para);
                //Start timer
                StartTimer();
    
                _mfrmProgressForm.StartPosition = FormStartPosition.CenterParent;
                _mfrmProgressForm.ShowDialog();
            }
    

到這裏,整個的進度條控件已經完成了!

調用

  • 定義一個參數類
	/// <summary>
    /// Para Class
    /// </summary>
    public class ParaArg
    {
        public DataTable Data { get; set; }
        public string Msg { get; set; }
        public Exception Ex { get; set; }
    }
  • 定義全局的幫助類BackgroundWorkerEx<ParaArg> workHelper = null;
  • 調用
				if (workHelper != null || (workHelper != null && workHelper.IsBusy))
                {
                    workHelper.Dispose();
                    workHelper = null;
                }
                if (workHelper == null)
                {
                    workHelper = new BackgroundWorkerEx<ParaArg>();
                }

                workHelper.DoWork += (eve) =>
                {
                    ParaArg args = eve.Argument;

                    try
                    { 
                        //ToDo  like Thread.Sleep(20000);
                        Thread.Sleep(10000);
                        args.Msg = "...this is bussiness code result";
                        throw new Exception("");
                    }
                    catch (Exception ex)
                    {
                        args.Ex = ex;
                    }
                    finally
                    {
                        eve.Result = args;
                    }

                };
                workHelper.RunWorkCompleted += (eve) =>
                {
                    if (eve.Error != null)
                    {
                        //get .net backgroundworker exception;
                        //handle this exception;
                        //return ?
                    }

                    //get your para result
                    ParaArg x = eve.Result;
                 
                    if (x.Ex != null)
                    {
                        //get your bussiness exception;
                        //handle this exception;
                        //return ?
                    }

                    //finially get your need;
                    //MayBe to do some UI hanlde and bussiness logical
                    string sReusltMsg = x.Msg;
                };

                workHelper.WorkStoped += (eve) =>
                { 
                    //if stoped ! it means no error;
                    //just get what you want; 
                    ParaArg x = eve.Result as ParaArg;

                    btnBegin.Enabled = true;
                };

                //參數
                ParaArg arg = new ParaArg()
                {
                    Msg = "Msg"
                };

                workHelper.AsyncStart(arg);

最後

其實不管是封裝的過程,還是調用,可以說完全就是BackgroundWorker的方式,所以很多BackgroundWorker相關的地方我都沒有很詳細的去說明;只要看看這個異步模型,就能夠很好理解!大家有空也可以實現以下,有問題也可以詳細,我比較喜歡交流技術~~~

還有一點就是這個解決方案已經放上Github上了,歡迎大家拉下來用

  • GitHub地址 歡迎star/fork

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

台中搬家公司費用怎麼算?

擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司

SUV一定費油?最火10萬級國產SUV油耗排行榜!大氣又省油!_潭子電動車

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

日本、大陸,發現這些先進的國家已經早就讓電動車優先上路,而且先進國家空氣品質相當好,電動車節能減碳可以減少空污

3T發動機有希望成為新一代的主流黃金排量。此款遠景SUV雖然也是搭載吉利研發的1。3T渦輪增壓發動機,但調校參數與帝豪GS有點細微差別,馬力上多了4匹,最大扭矩則還是185牛米,如果將吉利遠景SUV的1。3T發動機與傳祺GS4的1。3T發動機相對比,你會發現動力上遠景SUV弱了很多,扭矩上足足少了17牛米,但遠景SUV本來就不是為了壓榨最大馬力而生的,它是往着低油耗,振動噪音控制,加速平順性這方面去轉移,整體的發動機噪音控製得蠻不錯,變速箱也搭載了CVT無級變速器(帝豪GS上為雙離合變速器),更加符合普通

“對於買車的人來說,買轎車還是買SUV一直是他們首先會考慮的問題,買轎車呢,則有更好的操控性和舒適性,卻沒有很大的使用空間,買SUV呢,則能夠有更大的家用空間,離地間隙更高,但是油耗卻高於同級別同排量的轎車,那麼在SUV,難道就沒有近似於轎車的油耗體現?這一期就簡單列了一下我國10萬級別自主品牌SUV的油耗排行榜,可供大家參考。

此次數據來源於幾百上千名車主的用車實測,百公里油耗為綜合路況平均值,其中寶駿560和眾泰T600皆為手動擋版本,從圖表中的數據簡單換算一下,如果油價按照5.6元/升來計算,百公里油耗9L的花費大概是5毛錢每公里,而排量在1.5T-1.8T這個範圍,油耗在10.19-11.79L的SUV,大概每公里花費是5.7-6.6元,能夠有如此高的燃油經濟性,其中與其發動機的熱效率,變速箱的傳動效率有着密切的關係,雖然這次排行的自主品牌SUV的排量並沒有統一,但是我們姑且按照最低油耗往最高排量排起。下面我們來簡單講解一下這10款車分別用的是什麼發動機技術。

寶駿560的1.5T渦輪增壓發動機嚴格按照通用汽車的動力總成開發流程,分別彙集了霍尼韋爾,博爾華納,博世等全球頂級供應商,充分利用熱-機動能轉換,提高進氣效率,減緩渦輪遲滯現象,最大扭矩高達230牛米,最大功率為150匹馬力,能夠在2000轉左右就達到其峰值扭矩,足以媲美2.4L自然吸氣發動機。

在高效燃燒技術上,寶駿560的1.5T發動機採用了DVVT進排氣門正時技術,高效燃燒室和最優壓縮比技術,官方百公里綜合油耗僅為6.8L,而車主實測的也僅為8.94L每百公里。憑藉此發動機技術,再加以比同級別更加實惠的價格,舒適的空間,使得寶駿560深受消費者熱寵,截止至8月份的銷量已經突破20萬輛大軍,位居SUV總銷量排行第二名。

早在2014年,吉利就曾憑藉其1.3T渦輪增壓發動機的“強勁動力,高效節能,靜音舒適”獲得了“中國心”年度十佳發動機的稱號,這也是吉利首款自主研發的小排量渦輪增壓發動機,低排量增壓發動機將會是未來響應節能減排號召的新趨勢。

集成了尖端渦輪增壓技術,VVT技術,正時鏈條面免維護技術等於一身,能夠發揮出高性能,輕量化,耐久且低油耗的優異特質,最大功率達到129匹馬力,峰值扭矩達到185牛米,動力表現接近於2.0L自然吸氣發動機,車主實測的百公里油耗僅為9L,既能符合節能環保的排放標準,又能夠滿足消費者對於動力與經濟性的雙重需求,帝豪GS這款跨界SUV,同時搭載雙離合變速器,能夠將動力更好的傳遞開來。

傳祺GS4一直穩居SUV銷量排行榜前三名,同樣也是突破了20萬輛的銷量大軍,其原先搭載着低排量的1.3T渦輪增壓發動機+雙離合變速器,但據市場反饋還是存在着一些頓挫的現象,後期傳祺GS4又出台了其1.5T(235T)渦輪增壓發動機,最大扭矩為235牛米,最大功率為152匹馬力。

這款1.5T發動機具備進排氣雙可變正時系統,可變機油泵,以及自家的GCCS燃燒室控制系統專利技術,從技術上可以說已經達到了國內的主流水平,雖然這款發動機沒有缸內直噴技術,但是通過進氣道,燃燒室和活塞頂部設計,同樣能夠形成強烈的滾流效應,達到不錯的燃燒效率,同時渦輪增壓器來自日本IHI公司,能夠改善渦輪遲滯,初段低扭不足的現象,而同時1.5T版本的雙離合調校的還是趨於平順,頓挫不嚴重,如果還是擔心的朋友,可以考慮吉利SUV的CVT無級變速器版本。

眾泰確實在外觀設計上滿足了國人很多對高端車的審美要求,同時眾泰汽車也多採用國產三菱發動機為動力總成,但眾泰T600這款1.5T渦輪增壓器發動機則是由上汽和通用共同研發的老牌子,又經過眾泰自主配備了渦輪增壓器,代號為“15S4G”。

最大功率為162匹馬力,最大扭矩為215牛米,車主朋友們在生活中油耗綜合路況實測的平均值為9.39L每百公里,百公里加速僅為10.4秒,動力還是可以的。

現在小排量渦輪增壓發動機確實是市場的發展趨勢,能夠響應節能環保,今年又能享受國家購置稅減半的優惠,還能夠保證一定動力輸出,1.3T介於1.0T與1.5T之間,退一步可求低油耗,進一步可求大動力,所以1.3T發動機有希望成為新一代的主流黃金排量。

此款遠景SUV雖然也是搭載吉利研發的1.3T渦輪增壓發動機,但調校參數與帝豪GS有點細微差別,

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

有別於一般網頁架設公司,除了模組化的架站軟體,我們的營業主軸還包含:資料庫程式開發、網站建置、網頁設計、電子商務專案開發、系統整合、APP設計建置、專業網路行銷。

馬力上多了4匹,最大扭矩則還是185牛米,如果將吉利遠景SUV的1.3T發動機與傳祺GS4的1.3T發動機相對比,你會發現動力上遠景SUV弱了很多,扭矩上足足少了17牛米,但遠景SUV本來就不是為了壓榨最大馬力而生的,它是往着低油耗,振動噪音控制,加速平順性這方面去轉移,整體的發動機噪音控製得蠻不錯,變速箱也搭載了CVT無級變速器(帝豪GS上為雙離合變速器),更加符合普通家庭的用車需求。

說到東南汽車,這無非是與三菱關係最緊密的廠家了,國產化的三菱發動機在中國自主品牌的前期發動機市場佔據着非常大的比例,其價格便宜,穩定性高,燃油效率高,深受各個廠家的歡迎,雖然說現在許多自主品牌像奇瑞吉利比亞迪都有其自主發動機,而東南DX7上依然還是使用着老牌子4A91代號的發動機,只不過加了一個渦輪增壓,變成了4A91T。

最大扭矩為200牛米,最大功率為156匹馬力,擁有三菱獨家MIVER可變氣門正時技術,鋁合金缸體等材料,傳動部分則搭載6擋手動和5擋手自一體變速箱。車主實測綜合路況的平均油耗為10.19L每百公里。

貌似說到此次專題的重點了,榮威RX5在市場的影響力非常大,受歡迎和關注度程度也非常之高,許多朋友都會這款半路殺出來的自主SUV的發動機怎麼樣,且聽慢慢說。榮威RX5 20T車型上搭載的是上汽集團的15E4E發動機,這款發動機與上汽通用別克旗下的1.5T發動機有着密不可分的關係,簡單來說,榮威RX5上面的這台1.5T是和別克君越的1.5T發動機在硬件上是一樣的,只是針對不同車型,廠家採用不同調校和耐久性測試而已。

在這款1.5T的發動機上,應用了眾多主流技術,其中包括中置直噴技術,高壓燃油噴射系統,變排量機油泵等技術,我們要知道,中置噴油嘴設計可比一般的側置好很多,燃油霧化效果能夠更好,對於提升動力和降低排放有諸多優勢,該款發動機最大功率為169匹馬力,最大扭矩為250牛米,這個數值在1.5T的市場中可以說是非常之高了。

長城算是在中國研發小排量渦輪增壓發動機較早的廠商了,基於在代號為GW4G15的1.5L自然吸氣發動機上進行改造,選用上海菱重生產的渦輪增壓器,分別有低功率和高功率版本,最大功率分別為133匹馬力和150匹馬力,峰值扭矩分別為188牛米和210牛米,高功率版本應在在哈弗H2,H6,H6 Coupe這三個版本上。

但其渦輪中冷器採用的是風冷式,比水冷式性能差一點,不過造價便宜,技術相對穩定,在燃油經濟方面,這款1.5T發動機只在進氣門側配備了可變正時技術,如果進氣和排氣側都採用了可變技術的話,那麼還能夠在此基礎上再繼續降低一點油耗。不過長城也在不斷努力完善其1.5T發動機,先已展示出其代號為GW4B15發動機(未量產),具備了缸內直噴,進排氣雙可變正時系統,最大功率達到163匹,峰值扭矩達到280牛米,所以喜歡哈弗的朋友們,敬請期待這一款發動機在後期的運用。

長安汽車從2005年開始自主研發發動機,經歷了10年左右的時間,利用自己的研發實驗室,先進的儀器設備先後研發出1.5T和1.8T發動機,而後者就搭載在長安CS75該車型上,最大功率為177匹馬力,最大扭矩為230牛米,鋁合金缸體材料,同排量競品發動機有大眾的EA888的1.8T發動機,但在扭矩和穩定性上,長安可能還略輸一籌。

燃油經濟性方面,採用了雙智能可變VVT配氣結構,渦輪增壓器來自美國博格華納生產,在1500轉的時候即可啟動渦輪,利用高滾流燃燒技術,平行冷卻等技術,在大幅度提升動力的同時,較好地改善了燃油經濟性,變速箱則採用6速手自一體。

每一款國產SUV上市時,勢必能夠引起消費者的極大關注,就像瑞虎7,榮威RX5和吉利博越一樣,博越1.8T的手動版本搭載的發動機與博瑞相同,而自動擋版本搭載的則是升級版的動力總成,最大功率184匹馬力,最大扭矩高達285牛米,這比長安CS75的動力總成要略勝一籌,提升了不少。

該發動機採用了一體式渦輪增壓器,BOSCH汽油直噴,靜音鏈條傳動,DVVT氣門可變正時系統,雙平衡軸設計等等,博越自動擋版本的充沛相信會讓許多消費者嗤之以鼻,再加上如果有購置稅補貼的話,與長安CS75的競爭中博越會顯得更有勝算,如今吉利品牌在中國的名聲已經打響了,各大廠家也紛紛推出利器,博越是否能夠以其自主品牌的發動機去克服重重困難,還得需要市場的考驗。

總結:

還是那句話,如今小排量渦輪增壓器已經是以後的發展趨勢,為了能夠響應高動力低油耗的雙重需求,購買1.3T或者1.2T甚至是1.0T的汽車,其實已經足夠家用,若對動力需要比較高的朋友們,儘管上1.5T,1.8T甚至是2.0T也是可以的,因為現在挺多發動機技術原來越先進,風阻係數越來越低,已經不像以前那麼耗油了,如果你身邊的朋友也看中這10款自主品牌SUV的話,那就和大家一起分享吧。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

有別於一般網頁架設公司,除了模組化的架站軟體,我們的營業主軸還包含:資料庫程式開發、網站建置、網頁設計、電子商務專案開發、系統整合、APP設計建置、專業網路行銷。

一文讓你快速上手 Mockito 單元測試框架_台中搬家

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

台中搬家公司推薦超過30年經驗,首選台中大展搬家

前言

在計算機編程中,單元測試是一種軟件測試方法,通過該方法可以測試源代碼的各個單元功能是否適合使用。為代碼編寫單元測試有很多好處,包括可以及早的發現代碼錯誤,促進更改,簡化集成,方便代碼重構以及許多其它功能。使用 Java 語言的朋友應該用過或者聽過 Junit 就是用來做單元測試的,那麼為什麼我們還需要 Mockito 測試框架呢?想象一下這樣的一個常見的場景,當前要測試的類依賴於其它一些類對象時,如果用 Junit 來進行單元測試的話,我們就必須手動創建出這些依賴的對象,這其實是個比較麻煩的工作,此時就可以使用 Mockito 測試框架來模擬那些依賴的類,這些被模擬的對象在測試中充當真實對象的虛擬對象或克隆對象,而且 Mockito 同時也提供了方便的測試行為驗證。這樣就可以讓我們更多地去關注當前測試類的邏輯,而不是它所依賴的對象。

生成 Mock 對象方式

要使用 Mockito,首先需要在我們的項目中引入 Mockito 測試框架依賴,基於 Maven 構建的項目引入如下依賴即可:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>3.3.3</version>
    <scope>test</scope>
</dependency>

如果是基於 Gradle 構建的項目,則引入如下依賴:

testCompile group: 'org.mockito', name: 'mockito-core', version: '3.3.3'

使用 Mockito 通常有兩種常見的方式來創建 Mock 對象。

1、使用 Mockito.mock(clazz) 方式

通過 Mockito 類的靜態方法 mock 來創建 Mock 對象,例如以下創建了一個 List 類型的 Mock 對象:

List<String> mockList = Mockito.mock(ArrayList.class);

由於 mock 方法是一個靜態方法,所以通常會寫成靜態導入方法的方式,即 List mockList = mock(ArrayList.class)。

2、使用 @Mock 註解方式

第二種方式就是使用 @Mock 註解方式來創建 Mock 對象,使用該方式創需要注意的是要在運行測試方法前使用 MockitoAnnotations.initMocks(this) 或者單元測試類上加上 @ExtendWith(MockitoExtension.class) 註解,如下所示代碼創建了一個 List 類型的 Mock 對象(PS: @BeforeEach 是 Junit 5 的註解,功能類似於 Junit 4 的 @Before 註解。):

/**
 * @author mghio
 * @date: 2020-05-30
 * @version: 1.0
 * @description:
 * @since JDK 1.8
 */
//@ExtendWith(MockitoExtension.class)
public class MockitoTest {

  @Mock
  private List<String> mockList;

  @BeforeEach
  public void beforeEach() {
    MockitoAnnotations.initMocks(this);
  }
}

驗證性測試

Mockito 測試框架中提供了 Mockito.verify 靜態方法讓我們可以方便的進行驗證性測試,比如方法調用驗證、方法調用次數驗證、方法調用順序驗證等,下面看看具體的代碼。

驗證方法單次調用

驗證方法單次調用的話直接 verify 方法后加上待驗證調用方法即可,以下代碼的功能就是驗證 mockList 對象的 size 方法被調用一次。

/**
 * @author mghio
 * @date: 2020-05-28
 * @version: 1.0
 * @description:
 * @since JDK 1.8
 */
@ExtendWith(MockitoExtension.class)
public class MockitoVerifyTest {

  @Mock
  List<String> mockList;

  @Test
  void verify_SimpleInvocationOnMock() {
    mockList.size();
    verify(mockList).size();
  }
}
驗證方法調用指定次數

除了驗證單次調用,我們有時候還需要驗證一些方法被調用多次或者指定的次數,那麼此時就可以使用 verify + times 方法來驗證方法調用指定次數,同時還可以結合 atLeast + atMost 方法來提供調用次數範圍,同時還有 never 等方法驗證不被調用等。

/**
 * @author mghio
 * @date: 2020-05-28
 * @version: 1.0
 * @description:
 * @since JDK 1.8
 */
@ExtendWith(MockitoExtension.class)
public class MockitoVerifyTest {

  @Mock
  List<String> mockList;

  @Test
  void verify_NumberOfInteractionsWithMock() {
    mockList.size();
    mockList.size();

    verify(mockList, times(2)).size();
    verify(mockList, atLeast(1)).size();
    verify(mockList, atMost(10)).size();
  }
}
驗證方法調用順序

同時還可以使用 inOrder 方法來驗證方法的調用順序,下面示例驗證 mockList 對象的 size、add 和 clear 方法的調用順序。

/**
 * @author mghio
 * @date: 2020-05-28
 * @version: 1.0
 * @description:
 * @since JDK 1.8
 */
@ExtendWith(MockitoExtension.class)
public class MockitoVerifyTest {

  @Mock
  List<String> mockList;

  @Test
  void verify_OrderedInvocationsOnMock() {
    mockList.size();
    mockList.add("add a parameter");
    mockList.clear();

    InOrder inOrder = inOrder(mockList);

    inOrder.verify(mockList).size();
    inOrder.verify(mockList).add("add a parameter");
    inOrder.verify(mockList).clear();
  }
}

以上只是列舉了一些簡單的驗證性測試,還有驗證測試方法調用超時以及更多的驗證測試可以通過相關官方文檔探索學習。

驗證方法異常

異常測試我們需要使用 Mockito 框架提供的一些調用行為定義,Mockito 提供了 when(…).thenXXX(…) 來讓我們定義方法調用行為,以下代碼定義了當調用 mockMap 的 get 方法無論傳入任何參數都會拋出一個空指針 NullPointerException 異常,然後通過 Assertions.assertThrows 來驗證調用結果。

/**
 * @author mghio
 * @date: 2020-05-30
 * @version: 1.0
 * @description:
 * @since JDK 1.8
 */
@ExtendWith(MockitoExtension.class)
public class MockitoExceptionTest {

  @Mock
  public Map<String, Integer> mockMap;

  @Test
  public void whenConfigNonVoidReturnMethodToThrowEx_thenExIsThrown() {
    when(mockMap.get(anyString())).thenThrow(NullPointerException.class);

    assertThrows(NullPointerException.class, () -> mockMap.get("mghio"));
  }
}

同時 when(…).thenXXX(…) 不僅可以定義方法調用拋出異常,還可以定義調用方法后的返回結果,比如 when(mockMap.get(“mghio”)).thenReturn(21); 定義了當我們調用 mockMap 的 get 方法並傳入參數 mghio 時的返回結果是 21。這裡有一點需要注意,使用以上這種方式定義的 mock 對象測試實際並不會影響到對象的內部狀態,如下圖所示:

雖然我們已經在 mockList 對象上調用了 add 方法,但是實際上 mockList 集合中並沒有加入 mghio,這時候如果需要對 mock 對象有影響,那麼需要使用 spy 方式來生成 mock 對象。

public class MockitoTest {

  private List<String> mockList = spy(ArrayList.class);

  @Test
  public void add_spyMockList_thenAffect() {
    mockList.add("mghio");

    assertEquals(0, mockList.size());
  }
}

斷點后可以發現當使用 spy 方法創建出來的 mock 對象調用 add 方法后,mghio 被成功的加入到 mockList 集合當中。

與 Spring 框架集成

Mockito 框架提供了 @MockBean 註解用來將 mock 對象注入到 Spring 容器中,該對象會替換容器中任何現有的相同類型的 bean,該註解在需要模擬特定bean(例如外部服務)的測試場景中很有用。如果使用的是 Spring Boot 2.0+ 並且當前容器中已有相同類型的 bean 的時候,需要設置 spring.main.allow-bean-definition-overriding 為 true(默認為 false)允許 bean 定義覆蓋。下面假設要測試通過用戶編碼查詢用戶的信息,有一個數據庫操作層的 UserRepository,也就是我們等下要 mock 的對象,定義如下:

/**
 * @author mghio
 * @date: 2020-05-30
 * @version: 1.0
 * @description:
 * @since JDK 1.8
 */
@Repository
public interface UserRepository {

  User findUserById(Long id);

}

還有用戶操作的相關服務 UserService 類,其定義如下所示:

/**
 * @author mghio
 * @date: 2020-05-30
 * @version: 1.0
 * @description:
 * @since JDK 1.8
 */
@Service
public class UserService {

  private UserRepository userRepository;

  public UserService(UserRepository userRepository) {
    this.userRepository = userRepository;
  }

  public User findUserById(Long id) {
    return userRepository.findUserById(id);
  }
}

在測試類中使用 @MockBean 來標註 UserRepository 屬性表示這個類型的 bean 使用的是 mock 對象,使用 @Autowired 標註表示 UserService 屬性使用的是 Spring 容器中的對象,然後使用 @SpringBootTest 啟用 Spring 環境即可。

/**
 * @author mghio
 * @date: 2020-05-30
 * @version: 1.0
 * @description:
 * @since JDK 1.8
 */
@SpringBootTest
public class UserServiceUnitTest {

  @Autowired
  private UserService userService;

  @MockBean
  private UserRepository userRepository;

  @Test
  public void whenUserIdIsProvided_thenRetrievedNameIsCorrect() {
    User expectedUser = new User(9527L, "mghio", "18288888880");
    when(userRepository.findUserById(9527L)).thenReturn(expectedUser);
    User actualUser = userService.findUserById(9527L);
    assertEquals(expectedUser, actualUser);
  }
}

Mockito 框架的工作原理

通過以上介紹可以發現, Mockito 非常容易使用並且可以方便的驗證一些方法的行為,相信你已經看出來了,使用的步驟是先創建一個需要 mock 的對象 Target ,該對象如下:

public class Target {

  public String foo(String name) {
    return String.format("Hello, %s", name);
  }

}

然後我們直接使用 Mockito.mock 方法和 when(…).thenReturn(…) 來生成 mock 對象並指定方法調用時的行為,代碼如下:

@Test
public void test_foo() {
  String expectedResult = "Mocked mghio";
  when(mockTarget.foo("mghio")).thenReturn(expectedResult);
  String actualResult = mockTarget.foo("mghio");
  assertEquals(expectedResult, actualResult);
}

仔細觀察以上 when(mockTarget.foo(“mghio”)).thenReturn(expectedResult) 這行代碼,首次使用我也覺得很奇怪,when 方法的入參竟然是方法的返回值 mockTarget.foo(“mghio”),覺得正確的代碼應該是這樣 when(mockTarget).foo(“mghio”),但是這個寫法實際上無法進行編譯。既然 Target.foo 方法的返回值是 String 類型,那是不是可以使用如下方式呢?

台中搬家公司費用怎麼算?

擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司

Mockito.when("Hello, I am mghio").thenReturn("Mocked mghio");

結果是編譯通過,但是在運行時報錯:

從錯誤提示可以看出,when 方法需要一個方法調用的參數,實際上它只需要 more 對象方法調用在 when 方法之前就行,我們看看下面這個測試代碼:

@Test
public void test_mockitoWhenMethod() {
  String expectedResult = "Mocked mghio";
  mockTarget.foo("mghio");
  when("Hello, I am mghio").thenReturn(expectedResult);
  String actualResult = mockTarget.foo("mghio");
  assertEquals(expectedResult, actualResult);
}

以上代碼可以正常測試通過,結果如下:

為什麼這樣就可以正常測試通過?是因為當我們調用 mock 對象的 foo 方法時,Mockito 會攔截方法的調用然後將方法調用的詳細信息保存到 mock 對象的上下文中,當調用到 Mockito.when 方法時,實際上是從該上下文中獲取最後一個註冊的方法調用,然後把 thenReturn 的參數作為其返回值保存,然後當我們的再次調用 mock 對象的該方法時,之前已經記錄的方法行為將被再次回放,該方法觸發攔截器重新調用並且返回我們在 thenReturn 方法指定的返回值。以下是 Mockito.when 方法的源碼:

該方法裏面直接使用了 MockitoCore.when 方法,繼續跟進,該方法源碼如下:

仔細觀察可以發現,在源碼中並沒有用到參數 methodCall,而是從 MockingProgress 實例中獲取 OngoingStubbing 對象,這個 OngoingStubbing 對象就是前文所提到的上下文對象。個人感覺是 Mockito 為了提供簡潔易用的 API 然後才製造了 when 方法調用的這種“幻象”,簡而言之,Mockito 框架通過方法攔截在上下文中存儲和檢索方法調用詳細信息來工作的。

如何實現一個微型的 Mock 框架

知道了 Mockito 的運行原理之後,接下來看看要如何自己去實現一個類似功能的 mock 框架出來,看到方法攔截這裏我相信你已經知道了,其實這就是 AOP 啊,但是通過閱讀其源碼發現 Mockito 其實並沒有使用我們熟悉的 Spring AOP 或者 AspectJ 做的方法攔截,而是通過運行時增強庫 Byte Buddy 和反射工具庫 Objenesis 生成和初始化 mock 對象的。
現在,通過以上分析和源碼閱讀可以定義出一個簡單版本的 mock 框架了,將自定義的 mock 框架命名為 imock。這裡有一點需要注意的是,Mockito 有一個好處是,它不需要進行初始化,可以直接通過其提供的靜態方法來立即使用它。在這裏我們也使用相同名稱的靜態方法,通過 Mockito 源碼:

很容易看出 Mockito 類最終都是委託給 MockitoCore 去實現的功能,而其只提供了一些面向使用者易用的靜態方法,在這裏我們也定義一個這樣的代理對象 IMockCore,這個類中需要一個創建 mock 對象的方法 mock 和一個給方法設定返回值的 thenReturn 方法,同時該類中持有一個方法調用詳情 InvocationDetail 集合列表,這個類是用來記錄方法調用詳細信息的,然後 when 方法僅返回列表中的最後一個 InvocationDetail,這裏列表可以直接使用 Java 中常用的 ArrayList 即可,這裏的 ArrayList 集合列表就實現了 Mockito 中的 OngoingStubbing 的功能。
根據方法的三要素方法名、方法參數和方法返回值很容易就可以寫出 InvocationDetail 類的代碼,為了對方法在不同類有同名的情況區分,還需要加上類全稱字段和重寫該類的 equals 和 hashCode 方法(判斷是否在調用方法集合列表時需要根據該方法判斷),代碼如下所示:

/**
 * @author mghio
 * @date: 2020-05-30
 * @version: 1.0
 * @description:
 * @since JDK 1.8
 */
public class InvocationDetail<T> {

  private String attachedClassName;

  private String methodName;

  private Object[] arguments;

  private T result;

  public InvocationDetail(String attachedClassName, String methodName, Object[] arguments) {
    this.attachedClassName = attachedClassName;
    this.methodName = methodName;
    this.arguments = arguments;
  }

  public void thenReturn(T t) {
    this.result = t;
  }

  public T getResult() {
    return result;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    InvocationDetail<?> behaviour = (InvocationDetail<?>) o;
    return Objects.equals(attachedClassName, behaviour.attachedClassName) &&
        Objects.equals(methodName, behaviour.methodName) &&
        Arrays.equals(arguments, behaviour.arguments);
  }

  @Override
  public int hashCode() {
    int result = Objects.hash(attachedClassName, methodName);
    result = 31 * result + Arrays.hashCode(arguments);
    return result;
  }
}

接下來就是如何去創建我們的 mock 對象了,在這裏我們也使用 Byte Buddy 和 Objenesis 庫來創建 mock 對象,IMockCreator 接口定義如下:

/**
 * @author mghio
 * @date: 2020-05-30
 * @version: 1.0
 * @description:
 * @since JDK 1.8
 */
public interface IMockCreator {

  <T> T createMock(Class<T> mockTargetClass, List<InvocationDetail> behaviorList);

}

實現類 ByteBuddyIMockCreator 使用 Byte Buddy 庫在運行時動態生成 mock 類對象代碼然後使用 Objenesis 去實例化該對象。代碼如下:

/**
 * @author mghio
 * @date: 2020-05-30
 * @version: 1.0
 * @description:
 * @since JDK 1.8
 */
public class ByteBuddyIMockCreator implements IMockCreator {

  private final ObjenesisStd objenesisStd = new ObjenesisStd();

  @Override
  public <T> T createMock(Class<T> mockTargetClass, List<InvocationDetail> behaviorList) {
    ByteBuddy byteBuddy = new ByteBuddy();

    Class<? extends T> classWithInterceptor = byteBuddy.subclass(mockTargetClass)
        .method(ElementMatchers.any())
        .intercept(MethodDelegation.to(InterceptorDelegate.class))
        .defineField("interceptor", IMockInterceptor.class, Modifier.PRIVATE)
        .implement(IMockIntercepable.class)
        .intercept(FieldAccessor.ofBeanProperty())
        .make()
        .load(getClass().getClassLoader(), Default.WRAPPER).getLoaded();

    T mockTargetInstance = objenesisStd.newInstance(classWithInterceptor);
    ((IMockIntercepable) mockTargetInstance).setInterceptor(new IMockInterceptor(behaviorList));

    return mockTargetInstance;
  }
}

基於以上分析我們可以很容易寫出創建 mock 對象的 IMockCore 類的代碼如下:

/**
 * @author mghio
 * @date: 2020-05-30
 * @version: 1.0
 * @description:
 * @since JDK 1.8
 */
public class IMockCore {

  private final List<InvocationDetail> invocationDetailList = new ArrayList<>(8);

  private final IMockCreator mockCreator = new ByteBuddyIMockCreator();

  public <T> T mock(Class<T> mockTargetClass) {
    T result = mockCreator.createMock(mockTargetClass, invocationDetailList);
    return result;
  }

  @SuppressWarnings("unchecked")
  public <T> InvocationDetail<T> when(T methodCall) {
    int currentSize = invocationDetailList.size();
    return (InvocationDetail<T>) invocationDetailList.get(currentSize - 1);
  }
}

提供給使用者的類 IMock 只是對 IMockCore 進行的簡單調用而已,代碼如下:

/**
 * @author mghio
 * @date: 2020-05-30
 * @version: 1.0
 * @description:
 * @since JDK 1.8
 */
public class IMock {

  private static final IMockCore IMOCK_CORE = new IMockCore();

  public static <T> T mock(Class<T> clazz) {
    return IMOCK_CORE.mock(clazz);
  }

  public static <T> InvocationDetail when(T methodCall) {
    return IMOCK_CORE.when(methodCall);
  }
}

通過以上步驟,我們就已經實現了一個微型的 mock 框架了,下面來個實際例子測試一下,首先創建一個 Target 對象:

/**
 * @author mghio
 * @date: 2020-05-30
 * @version: 1.0
 * @description:
 * @since JDK 1.8
 */
public class Target {

  public String foo(String name) {
    return String.format("Hello, %s", name);
  }

}

然後編寫其對應的測試類 IMockTest 類如下:

/**
 * @author mghio
 * @date: 2020-05-30
 * @version: 1.0
 * @description:
 * @since JDK 1.8
 */
public class IMockTest {

  @Test
  public void test_foo_method() {
    String exceptedResult = "Mocked mghio";
    Target mockTarget = IMock.mock(Target.class);

    IMock.when(mockTarget.foo("mghio")).thenReturn(exceptedResult);

    String actualResult = mockTarget.foo("mghio");

    assertEquals(exceptedResult, actualResult);
  }

}

以上測試的可以正常運行,達到了和 Mockito 測試框架一樣的效果,運行結果如下:

上面只是列出了一些關鍵類的源碼,自定義 IMock 框架的所有代碼已上傳至 Github 倉庫 imock,感興趣的朋友可以去看看。

總結

本文只是介紹了 Mockito 的一些使用方法,這隻是該框架提供的最基礎功能,更多高級的用法可以去官網閱讀相關的文檔,然後介紹了框架中 when(…).thenReturn(…) 定義行為方法的實現方式並按照其源碼思路實現了一個相同功能的簡易版的 imock 。雖然進行單元測試有很多優點,但是也不可盲目的進行單元測試,在大部分情況下只要做好對項目中邏輯比較複雜、不容易理解的核心業務模塊以及項目中公共依賴的模塊的單元測試就可以了。

參考文章

Mockito
Objenesis
Byte Buddy

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

台中搬家公司費用怎麼算?

擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司

能杜絕95%的變道追尾事故!這幾款9.98萬起的車型神了!_網頁設計公司

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

搬家費用:依消費者運送距離、搬運樓層、有無電梯、步行距離、特殊地形、超重物品等計價因素後,評估每車次單

5T 手動尊貴型配備了併線輔助系統,對於如此碩大的SUV來說,盲區更加值得關注,加上DX7的車窗設計得不算大,A柱也不小,因此開DX7真的要多注意注意,這個帶併線輔助系統的東南DX7 2017款 1。5T 手動尊貴型售價12。99萬,使用1。

日產軒逸

指導價:9.98-15.90萬

軒逸是我們熟悉的國民家轎之一,每個月的銷量都十分高,省油安靜平順是我們對軒逸的最大印象,當然軒逸的尺寸也不小,4631*1760*1503mm的車身尺寸,2700mm的軸距都意味着這是一台大緊湊型轎車,因此它的車內空間表現也十分不錯,不過1.6L的發動機的版本動力不怎麼強,這也是它的短板所在。

軒逸 2016款 1.6XV CVT智尊版上就裝備了併線輔助系統,而且不止併線輔助,這款指導價14.68萬的1.6XV CVT智尊版還配備了車道偏離預警系統和主動剎車系統,因此這款車的安全性裝備也是十分豐富,LED近光燈、ESp等裝備也有,配置上這款車型十分豐富。

東南DX7

指導價:9.69-13.99萬

東南DX7算是大家比較熟悉的SUV了,外觀設計是DX7最大的亮點,

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

透過選單樣式的調整、圖片的縮放比例、文字的放大及段落的排版對應來給使用者最佳的瀏覽體驗,所以不用擔心有手機版網站兩個後台的問題,而視覺效果也是透過我們前端設計師優秀的空間比例設計,不會因為畫面變大變小而影響到整體視覺的美感。

意大利設計大師操刀設計的造型看起來十分有氣場,加上4530*1900*1700mm的車身尺寸,2700mm的軸距營造出來的大車感十分充足,喜歡開大車的朋友一定不能錯過這車。

東南DX7 2017款 1.5T 手動尊貴型配備了併線輔助系統,對於如此碩大的SUV來說,盲區更加值得關注,加上DX7的車窗設計得不算大,A柱也不小,因此開DX7真的要多注意注意,這個帶併線輔助系統的東南DX7 2017款 1.5T 手動尊貴型售價12.99萬,使用1.5T發動機(156馬力/215牛米)搭配6擋手動變速箱,性價比還是很不錯的。分區空調/感應雨刷/GpS系統/車內氛圍燈/電動主駕駛座椅/前後雷達/全景影像也都有配備。

標緻308

指導價:9.97-15.97萬

如果說老款標緻308長得很帥的話,我想沒幾個人會贊同,但我說這一代308很帥的話,我想沒幾個人會反對,的確這代標緻308的造型相比老款發生了翻天覆地的變化,4590*1820*1488mm的車身尺寸不算小,尤其是1820mm的寬度使得整車看起來更加寬大,頗有幾分寬體轎跑既視感。

標緻308 2016款 1.6T 自動尊貴版指導價15.97萬,這款車採用了1.6T發動機加6擋手自一體變速箱的動力組合,這款車配備了併線輔助系統,搭配上法系車紮實的底盤,1.6T發動機動力強勁,因此標緻308十分適合跑高速。

本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

節能減碳愛地球是景泰電動車的理念,是創立景泰電動車行的初衷,滿意態度更是服務客戶的最高品質,我們的成長來自於你的推薦。

甘肅將推動長城國家文化公園建設_台中搬家

台中搬家公司費用怎麼算?

擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司

記者11日從甘肅省文物局獲悉,中國長城資源大省甘肅今年將推動長城國家文化公園建設,提升全省長城保護利用整體水平。同時啟動河西走廊國家遺產線路保護利用行動計劃,帶動文物保護利用區域協同發展。

甘肅境內長城修建始於戰國秦,其後歷代多有修築,現存遺迹主要是戰國秦、漢、明三代,三代長城的西端起點均在甘肅境內。全省長城共分佈在11個市州,總長度為3654千米,居全國第二。明長城1738千米,長度為全國之首。

根據甘肅長城國家文化公園建設規劃,結合甘肅長城資源稟賦和基礎條件,初步規劃“338”的總體布局,即:建設敦煌漢長城、嘉峪關明長城、臨洮戰國秦長城3個綜合示範區,漢長城、明長城、戰國秦長城3個集中展示帶,臨澤、永昌、民勤、古浪、天祝、景泰、環縣、華池8個重點展示區段。

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

台中搬家公司推薦超過30年經驗,首選台中大展搬家

甘肅省文物局黨組書記、局長馬玉萍表示,有關市縣要有計劃地加強全省長城保護利用,使更多的長城文物達到開放服務的標準,使長城大省真正能夠讓世界看得見、叫得響。要重視加強日常管護,全面排查長城本體安全隱患,實施搶救性保護工程。同時有計劃地解決長城保護範圍內的違建、耕種等問題,全面改善長城環境風貌。

河西走廊在中華文明史、中外交流史上佔有特殊而重要的價值地位,“穿越千年、歷久彌新”。2020年,甘肅還將實施好河西走廊國家遺產線路保護利用行動計劃,帶動文物保護利用區域協同發展。

馬玉萍介紹說,甘肅省文物部門認真梳理河西走廊文化要素,提出全國首個國家遺產線路項目,並編製了保護利用行動計劃,涉及酒泉、嘉峪關、張掖、金昌、武威、白銀6個市,通過探索實踐,為全國創建國家遺產線路保護利用示範體系,該項目已被甘肅省政府和國家文物局攜手計劃於今年啟動實施。(馮志軍)

本站聲明:網站內容來http://www.societynews.cn/html/wh/fq/,如有侵權,請聯繫我們,我們將及時處理

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

台中搬家公司推薦超過30年經驗,首選台中大展搬家

定價不貴,就上頂配!12萬自主精緻SUV這款車也不錯_台中搬家

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

台中搬家公司推薦超過30年經驗,首選台中大展搬家

乘坐空間中規中矩,一家三口乘坐不會有什麼太多的局促感,後排沒有配備空調出風口,這其實也是目前小編對於各自主品牌的廠家都想吐槽的一點,後排出風口的配置成本並不算高,既然自主品牌要更進一步打動群眾的購買慾望,多開一個風口不會增加什麼成本,更可以提高自身形象。

北汽紳寶,這個品牌一直存在着爭議,有人抱怨說它的銷售網絡分佈得太少,買到車了也很難對其進行較為完善的維護,也有人抱怨它的車標很難看。但是紳寶對於細分市場的消費者的注重程度從推出的車型上說還是比較具有誠意的,今天要說的X55,就是小編少有的直接推薦頂配車型的SUV。

撇開那個十分具有爭議性的車標不談,光是提及紳寶X55的外觀其實它是一款非常符合當下審美趨勢的城市SUV車型,燈組模具線條凌厲,前臉層次感豐富,營造出了一種非常敦實憨厚的形象,同時車身線條非常簡潔,配合上多幅式輪轂讓這台SUV具有一定的運動感。

如果說前臉是紳寶X55在設計中較為用心的地方,車尾的勾畫則顯得有些隨意了,後方的視覺感個人感覺略顯怪異,相較於車頭的精緻,紳寶X55的外觀評價則顯得有些虎頭蛇尾的意思。

紳寶X55的內飾用料算是比較厚道的,

台中搬家公司費用怎麼算?

擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司

這也是正常的,畢竟一個影響力不算大的品牌要打入消費者的內心,再使用全塑料的內飾那豈不是自找死路?紳寶X55採用全軟性的中控包裹材質讓車內的舒適感較為出色,而且功能性按鍵布局也非常符合常理,使用起來不會顯得突兀。

乘坐空間中規中矩,一家三口乘坐不會有什麼太多的局促感,後排沒有配備空調出風口,這其實也是目前小編對於各自主品牌的廠家都想吐槽的一點,後排出風口的配置成本並不算高,既然自主品牌要更進一步打動群眾的購買慾望,多開一個風口不會增加什麼成本,更可以提高自身形象。

紳寶X55的動力總成搭載的是一款來自瀋陽航天三菱的1.5T渦輪增壓發動機,這款代號為4A91T的發動機已經稱得上是自主品牌汽車的神機,很多車型都有了搭載,賬面參數150ps的最大馬力,210N.m的峰值扭矩,數據不算出色,但是日常使用絕對可以滿足需求。

編輯總結:前文所述,紳寶X55這款定位在年輕家庭用戶來說,小編推薦的車型是直接考慮頂配車型,也就是配置單上的1.5T豪華版,指導價格11.98萬,無論是科技性配置還是安全性配置也是做到了一步到位,對於一款12萬以內的SUV來說,還是擁有着不俗的性價比。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

台中搬家公司費用怎麼算?

擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司

這台車看起來值30多萬 “國產路虎攬勝”竟只賣10萬?_台中搬家

台中搬家公司費用怎麼算?

擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司

內飾相比霸氣時尚的前臉,內飾就相對有些落後了,中控面板很不和諧,像是加裝的感覺,好在在配色上面黑橙雙色的設計還是挺吸引眼球的,全系標配無鑰匙啟動但是沒有無鑰匙進入系統,還有車身穩定系統全系都沒有,另外胎壓監測、倒車影像、自動空調等配置還是很實用的。

相信不少消費者對卡威這個品牌並不熟悉,它旗下第一款車卡威K1是個皮卡,在2014年的時候就有了年銷量過萬輛的成績,當然啦在中國它的熱度當然是比不過SUV的,所以推出了旗下第二款車型-卡威W1,它的售價為9.48-10.33萬,採用非承載式車身這是主打越野的節奏嗎?一起來看一下吧!

外觀

喲喲喲,這前臉看上去怎麼那麼熟悉呢,是不是和福特F-150皮卡非常相似呢,看那造型獨特的前大燈,但是細節上面進行了改進,進氣格柵又是另一種風格,充滿了JEEp風格的豎狀格柵,整體輪廓還是蠻相似的,看上去相當霸氣,長*寬*高(mm)為4600*1855*1810mm,在競爭對手中屬於中等水平。

而車身側面和尾部造型與黃海旗勝F1有這很高的相似度,這也難怪因為黃海的配件供應商之前一直是卡威供應的,簡單的線條勾勒出比較飽滿的效果,LED光源的尾燈有些令人意外,

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

台中搬家公司推薦超過30年經驗,首選台中大展搬家

雙邊雙出的的排氣管也算和霸氣的前臉相互呼應。

內飾

相比霸氣時尚的前臉,內飾就相對有些落後了,中控面板很不和諧,像是加裝的感覺,好在在配色上面黑橙雙色的設計還是挺吸引眼球的,全系標配無鑰匙啟動但是沒有無鑰匙進入系統,還有車身穩定系統全系都沒有,另外胎壓監測、倒車影像、自動空調等配置還是很實用的。

空間

卡威W1的座椅造型比較普通,但是柔軟性不錯,舒適的良好,2730mm的軸距在同級別來說也算中上水平,後排空間比較寬敞,中間座椅都配備了獨立頭枕,中央扶手箱也沒有缺席,儲物空間日常夠用,不過後備箱開口離地間隙稍高,便利性不好,總的來說中規中矩吧。

動力

動力方面該車提供了2.0L(最大功率129馬力)/2.4L汽油發動機(最大功率143馬力),最大扭矩轉速在2500轉,分別為175牛米和200牛米,搭配5擋手動變速器,相對於競爭對手來說差距不少,懸挂則採用前雙橫臂后五連桿整體橋式結構,加上非承載式車身,越野能力是否出色咋們拭目以待。

總結:外觀模仿痕迹還是較為明顯,霸氣威猛的外觀還是挺符合當下審美觀,內飾造型用料稍微遜色很多,但門板皮革包裹等細節做得很細心,動力上的不足應該是迫在眉睫的問題。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

台中搬家公司費用怎麼算?

擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司