顯示具有 C# 標籤的文章。 顯示所有文章
顯示具有 C# 標籤的文章。 顯示所有文章

2021年11月5日 星期五

C# tabControl停駐於父容器

標題:如何將C#的tabControl停駐於父容器

副標題:如何將C#的控制項停駐於父容器


今天使用C#的tabControl時,發現視窗大小改變後,tabControl控制項將無法跟著改變(會出現空隙)。



怎麼辦呢,首先,去tabControl的控制項屬性,找到Dock的屬性後,把None的選項改成Fill(選中間的矩形區域)


選好後,該控制項就能隨著父容器或視窗大小改變了。


2020年3月6日 星期五

[不專業評比]各主流程式語言效能比較

本篇將依據各主流程式語言執行的時間來比較效能差異

程式語言的速度僅供參考,效能往往取決於譯器而非語言本身


系統環境:

  • 作業系統:Windows10,64位元
  • CPU:intel i7 9700
執行環境:
  • Java:cmd
  • JavaScript:Chrome
  • C++:Visual Studio 2019
  • C#:Visual Studio 2019
  • Python:Spider
================
比較:各語言的IO效能

程式碼:
JAVA
  • for (var i=0;i<10000;i++)System.out.print(1);  
JavaScript
  • for (var i=0;i<10000;i++)console.log(1);
C++
  • for(int i=0;i<10000;i++) std::cout << 1;
C#
  • for (int i = 0; i < 10000; i++) Console.Write(1);
Python
  • for i in range(10000):
  •     print(1)

執行時間(單位:毫秒)

Java130
JavaScript73
C++176
C++(最佳化)129
C#136
C#(最佳化)132
Python142

=================
比較:各語言的一維陣列/串列填充效能

程式碼
JAVA
  • for (var i=0;i<1000000;i++)arr[i]=i; 
JavaScript
  • for (var i=0;i<1000000;i++)arr[i]=i;
C++
  • for (int i = 0; i < 1000000; i++)
  • number[i]=i;
C#
  • for (int i = 0; i < 1000000; i++) number[i]=i;
Python
  • for i in range(1000000):
  •     list0[i]=i


執行時間(單位:毫秒)

Java2
JavaScript6
C++2
C++(最佳化)2
C#4
C#(最佳化)2
Python98

=============================
比較:各語言的串列新增效能

程式碼
JAVA
  • for (int i = 0; i < 1000000; i++) st.push(i); 
JavaScript
  •  for (var i=0;i<10000000;i++)list.push(i);
C++
  • for (int i = 0; i < 1000000; i++)
  • mystack.push(i);
C#
  • for (int i = 0; i < 1000000; i++) myStack.Push(i); 
Python
  • for i in range(1000000):
  •     list0.append(i)

執行時間(單位:毫秒)

Java20
JavaScript135
C++690
C++(最佳化)12
C#59
C#(最佳化)59
Python94

========================
比較:各語言的遞迴效能(使用費氏數列)

程式碼
JAVA
  • public static long fib(int n){
  •         if(n==1)
  • return 1;
  • else if (n==2)
  • return 1;
  • else
  • return fib(n-1)+fib(n-2); 
  •     }   
  • fib(40);
JavaScript
  • function fib(n){
  •       if (n == 1)
  •         return 1;
  •       else if (n == 2)
  •         return 1;
  •       else
  •         return fib(n - 1) + fib(n - 2);
  •     }
  • fib(40);
C++
  • static long fib(int n) {
  • if (n == 1)
  • return 1;
  • else if (n == 2)
  • return 1;
  • else
  • return fib(n - 1) + fib(n - 2);
  • }
  • std::cout<<fib(40);
C#
  • static long fib(int n)
  •         {
  •             if (n == 1)
  •                 return 1;
  •             else if (n == 2)
  •                 return 1;
  •             else
  •                 return fib(n - 1) + fib(n - 2);
  •         }
  • fib(40);
Python
  • def fib(n):
  •     if n == 1:
  •         return 1
  •     elif n == 2:
  •         return 1
  •     else:
  •         return fib(n - 1) + fib(n - 2); 
  • fib(40)

執行時間(單位:毫秒)

Java224
JavaScript632
C++2527
C++(最佳化)251
C#2035
C#(最佳化)343
Python17947

================
比較:各語言大型二維陣列效能

程式碼(灰色字不列入計時):
JAVA

  • int arr[][]; 
  •  arr = new int[10000][10000]; 
  • for(int i = 0; i < arr.length; i++)  
  •             for(int j = 0; j < arr[i].length; j++) 
  •                 arr[i][j] = 0; 
  • for(int i = 0; i < arr.length; i++)  
  •             for(int j = 0; j < arr[i].length; j++) 
  •                 arr[i][j] = 255-arr[i][j]; 
JavaScript
  • var tArray = new Array();
  •     for (var k = 0; k < 10000; k++) {
  •       tArray[k] = new Array();
  •       for (var j = 0; j < 10000; j++) {
  •         tArray[k][j] = 0;
  •       }
  •     }
  •     var y=0,x=0;
  •  for (y = 0; y < 10000; y++) {
  •       for (x = 0; x < 10000; x++) {
  •         tArray[y][x] = 255 - tArray[y][x];
  •       }
  •     }
C++
  • unsigned char** fp = new unsigned char* [10000];
  • int x = 0, y = 0;
  • for (int j = 0; j < 10000; j++)
  • fp[j] = new unsigned char [10000];
  • for (y = 0; y < 10000; y++)
  • {
  • for (x = 0; x < 10000; x ++)
  • {
  • fp[y][x] = 255 - fp[y][x];
  • }
  • }

C#
  • int[,] number = new int[10000, 10000];
  •             int y = 0, x = 0;
  •             for (y = 0; y < 10000; y++) for (x = 0; x < 10000; x++) number[y,x] = 0;
  • for (y = 0; y < 10000; y++)
  • {
  • for (x = 0; x < 10000; x++)
  • {
  •  number[y,x] = 255 - number[y,x];
  • }
  •  }
Python
  • score=[[0]*10000 for i in range(10000)]
  • for i in range(10000):
  •     for j in range(10000):
  •         score[i][j]=0
  • for i in range(10000):
  •     for j in range(10000):
  •         score[i][j]=255-score[i][j]

執行時間(單位:毫秒)

Java52
JavaScript194
C++185
C++(最佳化)45
C#437
C#(最佳化)171
Python13908

=======================
整體上來說,在各個方面每個語言各有優劣

但依我個人排序的順序比為:
  1. C++(最佳化)
  2. Java
  3. C#(最佳化)
  4. JavaScript
  5. Python
不過就如開頭紅字所說,程式語言不應用來直接比較速度,如果想要更優的效能,編寫出更好的演算法帶來的速度效益往往超過更換語言的速度效益

對此篇有興趣的歡迎指教,希望以後可以出更多IT相關文章

2019年6月23日 星期日

C#呼叫C++進行影像處理

當使用C#進行影像處理的時候,如果遇上了需要大量運算的部分,C#可能不太夠力(C#的指標運算受到了限制而且在運算過程中做了很多邊界檢查),這時可以考慮呼叫C++的函式處理這些影像。

在開始之前可以先參考這篇文章:
https://corettainformation.blogspot.com/2019/06/cc.html



我們以C# Window Forms App作為範例。




我們拉一個Panel(裝PictureBox用的容器)進表單中,然後讓它填滿整個表單。

然後拉一個PictureBox(圖片),讓它填滿表單,樣式設定為Zoom(這樣就可以等比例縮放影像)

接著拉一個MenuStrip(選單)進表單中,設定兩組選項("載入影像"和"處理")


最後拉一個openFileDialog(開啟檔案用的對話方塊)進表單中。


這樣子需要的元件已經都佈置好了。


然後在表單的程式碼中新增一個Bitmap(點陣圖)


回到表單設計頁面,將"載入影像"點兩下,再次進入程式碼編輯器中。

鍵入以下程式碼


private void 載入影像ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            openFileDialog1.Filter = "圖片檔 (*.png;*.jpg;*.bmp;*.gif;*.tif)|*.png;*.jpg;*.bmp;*.gif;*.tif";
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                bitmap = new Bitmap(openFileDialog1.FileName);//將選擇的影像載入至bitmap中
                pictureBox1.Image = bitmap;//將bitmap顯示在pictureBox中
            }
        }

解說:
openFileDialog1.Filter可限制檔案的類型,我們選png、jpg、bmp、gif、tif這五種。
使用openFileDialog1.ShowDialog()可開啟瀏覽檔案的對話方塊,當DialogResult.OK(開啟成功)時會將檔案路徑(openFileDialog1.FileName)傳給Bitmap產生新的點陣物件,我們將這個點陣影像指派給bitmap,最後設定PictureBox的影像為這個點陣圖。



接著測試程式是否能正常執行,執行程式後,按下載入影像/選擇要瀏覽的圖片/按下開啟舊檔。



如果能正常顯示影像,代表到目前為只是成功的,可以繼續做下去。


回到表單設計頁面,點兩下"處理",進入程式碼編輯器中。


接著鍵入以下程式碼。
註:必須在標頭引入 using System.Drawing.Imaging;

Bitmap MyNewBmp = bitmap;
Rectangle MyRec = new Rectangle(0, 0, MyNewBmp.Width, MyNewBmp.Height);
BitmapData MyBmpData = MyNewBmp.LockBits(MyRec, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
unsafe
{
          //empty
}
MyNewBmp.UnlockBits(MyBmpData);
pictureBox1.Image = MyNewBmp;

解說:
Rectangle可以是一種矩形範圍,此範例為(0,0)的座標到(總寬度,總高度)的矩形範圍(意即選取了整張影像)
BitmapData 是像素資料。
LockBits可將點陣影像鎖定在系統記憶體內,可提高處理效能(但是最後務必使用UnlockBits來解鎖)。
MyNewBmp.LockBits的參數中第一個放入MyRec(剛剛的矩形範圍),第二個是數字3(ImageLockMode是列舉,其中的ReadWrite是列舉值3,代表這個BitmapData可以讀取也可以寫入),第三個參數是PixelFormat的列舉值,Format32bppArgb代表32位元的影像格式(包含Alpha、R、G、B的色彩通道)。

unsafe是C#要使用非安全性代碼時必須使用的關鍵字,此部分先空著,等一下會在這個括號裡加東西。
UnlockBits是用來解鎖LockBits鎖住的記憶體,這是必要的東做,不做的話有可能會出現難以預期的錯誤。
最後將MyMyNewBmp顯示在pictureBox上

註:如果無法使用unsafe的話,請對專案按右鍵/屬性,點選建置/勾選允許不安全的程式碼。



接著我們開始製作C++函式來給C#調用。

對方案按下右鍵/加入/新增專案

嘗試尋找一個C++的CLR類別庫專案。
註:如果找不到的話請見下一張圖。

如果沒有找到的話就必須先安裝CLI的套件。


新增完成後,進入C++的程式碼編輯器中。


接著在類別中新增以下函式:

public:
void inline colorTo255(unsigned char* ptr, int width, int height, int channel)
{
unsigned char** fp = new unsigned char* [height];
int Stride = width * channel, x = 0, y = 0;
for (int j = 0; j < height; j++)
fp[j] = ptr + (Stride * j);
for (y = 0; y < height; y++)
{
for (x = 0; x < Stride; x += channel)
{
fp[y][x] = 255 - fp[y][x];
fp[y][x + 1] = 255 - fp[y][x + 1];
fp[y][x + 2] = 255 - fp[y][x + 2];
}
}
delete[] fp;
}

解說:
我們會在稍後引入點陣圖第0個像素的指標(ptr),其他參數包括width(影像寬度)、height(影像高度)、channel(通道數,有ARGB四種,所以此範例中的值應為四)
Stride是指掃描寬度(影像的每一列有多少位元組),x、y為等一下會用到的座標。
在 for (int j = 0; j < height; j++)fp[j] = ptr + (Stride * j); 中,我們將fp設定為影像中每一列開頭的指標位置的指標。
在外層迴圈,為逐列掃描,一個影像有幾列高度(height)就有多高。
在內層迴圈,為逐行掃描,每次增加4個位元組。
fp[y][x] = 255 - fp[y][x]; 將影像中的(x,y)座標的B值反轉。
fp[y][x + 1] = 255 - fp[y][x + 1];將影像中的(x,y)座標的G值反轉。
fp[y][x + 2] = 255 - fp[y][x + 2];將影像中的(x,y)座標的R值反轉。
註:在這裡,色彩空間的排列方式為BGRA(意即fp[y][x + 3]會是Alpha值)
結束後,使用 delete[] fp; 刪除不再需要用到的fp


 然後我們對這個C++專案按右鍵/建置。

接著對C#專案按右鍵/加入/參考。


選擇剛剛建立的C++專案,按下確定。

加入完參考後就可以讓C#呼叫剛剛建立的類別了,我們回到C#的程式碼編輯器中,在表頭加入剛剛的名稱空間,並新增C++物件。

using ClassLibrary2;
註:意思就是引入剛剛在C++程式碼中出現的「namespace ClassLibrary2」

接著回到處理影像的那部分(unsafe那裡),輸入以下程式碼:

private void 處理ToolStripMenuItem_Click(object sender, EventArgs e)
{
      Bitmap MyNewBmp = bitmap;
      Rectangle MyRec = new Rectangle(0, 0, MyNewBmp.Width, MyNewBmp.Height);
      BitmapData MyBmpData = MyNewBmp.LockBits(MyRec, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
       unsafe
       {
            C.colorTo255((byte*)MyBmpData.Scan0, MyNewBmp.Width, MyNewBmp.Height, 4);
        }
        MyNewBmp.UnlockBits(MyBmpData);
        pictureBox1.Image = MyNewBmp;
}

解說:
colorTo255是剛剛建立的C++函式,MyBmpData.Scan0是MyBmpData的第0個像素(我們將它轉換成byte*),MyNewBmp.Width是影像寬度,MyNewBmp.Height是影像高度 ,4是指通道數(包含ARGB四個通道)。


最後可以來測試程式是否正確了,我們按照剛剛的步驟執行程式,試著載入圖片。



然後按下選單中的處理。


如果看到影像的色彩被反轉了,恭喜你,成功了。

=====分隔線=====
如果覺得這篇文有幫助到你們的話,請留言或幫忙按個廣告吧。

您的支持是我寫文的最大動力。

如果依然失敗了,也請留言讓我知道,看看哪個環節出了問題,我有看到就會回。


希望這篇文有幫助到各位ξ( ✿>◡❛)

2019年6月2日 星期日

使用C#呼叫C++類別庫

本篇將介紹如何使用C#呼叫C++的類別並使用。

我們以C# Window Forms App作為範例。


新增Windows Form專案後,對方案按下右鍵/加入/新增專案

嘗試尋找一個C++的CLR類別庫專案。
註:如果找不到的話請見下一張圖。

如果沒有找到的話就必須先安裝CLI的套件。

新增完成後,進入C++的程式碼編輯器中。
我們在Class1類別中新增add()函數:
namespace ClassLibrary1 {
public ref class Class1
{
public:
int add(int number1, int number2)
{
return number1 + number2;
}
};
}
註:這個函數將回傳兩個參數加起來後的值。

接著對此專案按下右鍵/建置

建置完成後對著C#的專案按下右鍵/加入/參考

選擇剛剛建立的C++專案,按下確定。

加入完參考後就可以讓C#呼叫剛剛建立的類別了,我們進入最初建立的Windows Form表,對著表單點兩下進入C#的程式碼編輯器。

然後在表頭加入剛剛的名稱空間:
using ClassLibrary1;
註:意思就是引入剛剛在C++程式碼中出現的「namespace ClassLibrary1」

接著在Form1_Load中新增剛剛建立的Class1類別:
 Class1 c = new Class1();

然後使用Class1將自己定義的5跟10這兩個數字加起來:
int number = c.add(5, 10);

最後使用MessageBox顯示這個數字:
MessageBox.Show("" + number);

註:因為C#的字串類別的運算子有重載過,在字串後使用加號會自動將之後的型別轉成字串,所以我們用引號+數字就可以正常顯示該數字,並不需要大費周章的使用:
MessageBox.Show(Convert.ToString(number));


輸入完成之後按下執行:
如果看到剛才輸入的兩個數字加起來後的值,恭喜你,呼叫成功了。

=====分隔線=====
如果覺得這篇文有幫助到你們的話,請留言或幫忙按個廣告吧。

您的支持是我寫文的最大動力。

如果依然失敗了,也請留言讓我知道,看看哪個環節出了問題,我有看到就會回。


希望這篇文有幫助到各位ξ( ✿>◡❛)

2019年2月7日 星期四

[資料結構]關於陣列效能的改善

本篇文章探討陣列元素讀取的順序對效能的影響。

首先我們來看看這段用C++編寫的程式碼


解析:
我們在第5到第8行利用指標建構了一個8192*8192的陣列,隨後在第10行做起動計時器的動作。

在第11到第13行,我們利用迴圈將陣列填滿0。

在第14行,我們停止計時器,計算填滿陣列所花費的時間。

執行結果如下

此次填滿陣列花費了136毫秒


接著,我們對調行與列的位置,然後再執行一次程式

執行結果如下


此次填滿陣列花費了823毫秒,比上一次多花費了687毫秒

同樣都是填滿陣列的動作,為什麼會有如此可觀的時間差距呢。

以第一支程式來看,填滿陣列的動作是由左至右執行。


但是,第二支程式將行列的位置對調了,執行的順序變成由上至下執行。


這樣會造成什麼影響呢,從程式碼的第5到第8行中,可看出我們創造出的是以列為主的陣列。

在第一支程式中,一次填滿一列,填完一列再填滿下一列,只需花費136毫秒。

但是在第二支程式中,每填完一個元素,指標位置就得挪到下一列,直到填完整行元素後,指標位置才回到第一列的位置,浪費了不少移動時間。