1.int/int 返回值是int,例如:
static void Main(string[] args)
{
int a = 10;
int b = 3;
Console.WriteLine((a / b));//返回值是3
Console.Read();
}
想換回3.3怎麼辦?將b轉換成double:
static void Main(string[] args)
{
int a = 10;
int b = 3;
Console.WriteLine((a / (double)b));//返回值是3.3333333
Console.Read();
}
2.Dapper使用事物時,必須先將Connection打開,否則會報錯:
using (var connection = ConnectionFactory.GetConnection())
{
connection.Open();
var trans = connection.BeginTransaction();
connection.Execute( "insert into table1 values(@a,@b,@c)" ,obj1s, trans);
trans.Commit();
}
注意一定要connection.Open();如果不使用事物的話就無所謂了
3.class 與 struct的區別:class是引用類型,struct是值類型,區別如下代碼:
聲明一個類:
public class PersonClass
{
public string Name { get; set; }
public int Age { get; set; }
public override string ToString()
{
return string.Format("Name:" + Name + ",Age:" + Age.ToString());
}
}
聲明一個結構:
public struct PersonStruct
{
public string Name { get; set; }
public int Age { get; set; }
public override string ToString()
{
return string.Format("Name:" + Name + ",Age:" + Age.ToString());
}
}
測試代碼如下:
單個對象測試:
static void Main(string[] args)
{
//pc2會隨着pc1的變化而變化,他們指向的是同一個內存
Console.WriteLine("Class Test:");
PersonClass pc1 = new PersonClass();
pc1.Age = 1;
PersonClass pc2 = pc1;
Console.WriteLine(pc2);
pc1.Age = 2;
Console.WriteLine(pc2);
//ps2並不會隨着ps1的變化而變化,他們指向的是兩塊不同的內存
Console.WriteLine("Struct Test:");
PersonStruct ps1 = new PersonStruct();
ps1.Age = 1;
PersonStruct ps2 = ps1;
Console.WriteLine(ps2);
ps1.Age = 2;
Console.WriteLine(ps2);
Console.Read();
}
運行結果如下:
數組測試:(注意有坑)
static void Main(string[] args)
{
PersonStruct[] personStructs = new PersonStruct[2];
for (int i = 0; i < personStructs.Length; i++)
{
personStructs[i].Age = i;
}
PersonClass[] personClasses = new PersonClass[2];
for (int i = 0; i < personClasses.Length; i++)
{
personClasses[i].Age = i;//此處會報錯,因爲沒有實例化
}
}
運行結果如下:
聲明瞭一個容量爲2的數組,在沒有給數組的元素賦值時,數組元素爲類型的默認值
結構體是值類型,初始值爲結構中各個值爲初始狀態的對象,而類是引用類型,初始值爲null,這也就解釋了上面一樣的寫法,爲什麼類會報錯的原因,截圖如下:
4.UDP通訊:在ReceiveFrom調用前,要調用Bind()方法或者SendTo,否則會報沒有綁定本地終結點。調用SendTo方法時,如果沒有調用Bind()方法,會自動分配一個本地終結點,但是主動調用Bind要給一個確定的終結點,這個終結點可能會被其他程序正在使用。如下:
這種寫法會拋異常:
var targetEP = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12345);
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
byte[] bytes = new byte[1024];
EndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);
int count = socket.ReceiveFrom(bytes, ref remoteEP);//會拋出沒有綁定本地終結點的異常
解決的辦法就是在調用ReceiveFrom之前調用一次SendTo或者Bind:
var targetEP = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12345);
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.SendTo(Encoding.UTF8.GetBytes("hello,i am client"),targetEP);//會自動給socket分配一個本地終結點
byte[] bytes = new byte[1024];
EndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);
int count = socket.ReceiveFrom(bytes, ref remoteEP);
5.在For循環中,開啓線程要採用帶參數的,否則,後果如下:
正確的姿勢爲:
6.使用NPOI時,注意樣式(CellStyle)的實例不要太多,否則容易報錯。
讀取內容時,要讀取到最後一行,必須<= worksheet.LastRowNum:
7.關於C# Winform的:
設置窗體的this.ShowInTaskbar = false會觸發窗體上的子控件的Load事件,如下圖所示:
點擊按鈕,設置窗體的ShowTaskbar=false時,會再次觸發UserControl的Load事件,
再設置ShowTaskbar=true時,會再次觸發UserControl的Load事件,所以在Load裏寫代碼要小心,並不一定Load事件只會在窗體加載時觸發一次。
個人感覺是:當窗體的ShowTaskbar屬性變化時,會將窗體的子控件全部Load一遍
8.操作INI時,INI的路徑要使用絕對路徑,不能使用相對路徑,否則會寫入或者讀取不成功!
封裝一個操作INI的方法:
public class CommonMethod
{
static object lockObj = new object();
[DllImport("kernel32")]
private static extern long WritePrivateProfileString(string section, string key, string val, string filePath);
[DllImport("kernel32")]
private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath);
public static void IniWriteValue(string sectionName, string key, string value,string fileName)
{
WritePrivateProfileString(sectionName, key, value, fileName);
}
/// <summary>
/// 讀取INI文件的內容,內容的默認長度爲1000
/// </summary>
/// <param name="section"></param>
/// <param name="key"></param>
/// <param name="fileName"></param>
/// <param name="size"></param>
/// <returns></returns>
public static string IniReadValue(string section, string key, string fileName,int size=1000)
{
try
{
StringBuilder stringBuilder = new StringBuilder(size);
GetPrivateProfileString(section, key, "", stringBuilder, size, fileName);
return stringBuilder.ToString().Trim();
}
catch (Exception e)
{
throw;
}
}
}
使用:
string iniPath = "test.ini";
CommonMethod.IniWriteValue("Test", "Count", "3", iniPath);
Console.Read();
運行完,發現程序目錄沒有test.ini,也沒有寫入任何東西:
當把上面的代碼改爲:
string iniPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "test.ini");
CommonMethod.IniWriteValue("Test", "Count", "3", iniPath);
Console.Read();
運行,發現能寫進去:
綜上,INI操作時,INI的路徑一定要用絕對路徑,不要用相對路徑,這和操作txt文件不一樣!!
9.操作List的時候要注意以下兩點:
不能在Foreach的時候移除被遍歷集合的元素!!!;
最好不要在For循環的時候移除被循環集合的元素!!!;
經常看到別人寫的C#代碼,上來就是一個Try…Catch,然後Try裏做的事情就是:
try
{
foreach(var i in list)
{
//dosomthing..
list.Remove(i)
}
}
catch()
{}
哪些場景需要遍歷集合的元素,然後移除呢?比如對設備進行發送指令,有輪詢設備狀態的指令,有手動復位的指令,有其他手動需要發送的指令,手動發送指令的時候,肯定這個時候不能發送輪詢狀態,這個時候手動發送的指令優先級最高,需要一個list來裝指令,手動的指令就放在list的最前面,然後一個線程去消費這個list。當另一個線程檢測到list有指令的時候就需要把指令取出來發送給設備,取出指令的過程:
遍歷集合-》獲取一個元素(一條指令)-》發送設備指令-》移除這個指令
這裏就涉及到遍歷移除操作,於是就有上面的代碼。
我的解決方法是:(雖然沒有更好的辦法)
var list_copy=list.ToList();//複製一個list
foreach(var i in list_copy)
{
//dosomthing..
list.Remove(i);
}
至於爲什麼最好不要在For循環的時候移除被循環集合的元素,大家可以試驗一下
參考這個文章的:https://blog.csdn.net/z15732621582/article/details/73896321?locationNum=10&fps=1
10.單例模式時,遍歷屬性調用ToString時需要注意,是否會造成無限遞歸。
請看如下有BUG的代碼:
class Program
{
static void Main(string[] args)
{
Person p = Person.Instance;
p.Name = "Tony";
p.Age = 111;
Console.WriteLine(p);
Console.ReadLine();
}
}
class Person
{
private static object lockObj = new object();
private static Person instance;
private Person()
{
}
public int Age { get; set; }
public string Name { get; set; }
public static Person Instance
{
get
{
if (instance == null)
{
lock (lockObj)
{
if (instance == null)
{
instance = new Person();
}
}
}
return instance;
}
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
foreach (var prop in this.GetType().GetProperties())
{
sb.AppendFormat("{0}:{1};",prop.Name,prop.GetValue(this,null));
}
return sb.ToString();
}
}
運行效果如下:
爲什麼?
因爲this.GetType().GetProperties()會把Instance也獲取到,然後調用Instance的ToString方法,從而一直遞歸循環
要解決這種BUG有兩種方法。
1.獲取到是Instance時就不管:
public override string ToString()
{
StringBuilder sb = new StringBuilder();
foreach (var prop in this.GetType().GetProperties())
{
if (prop.Name == "Instance")
{
continue;//跳過Instance
}
sb.AppendFormat("{0}:{1};",prop.Name,prop.GetValue(this,null));
}
return sb.ToString();
}
方法2:我們觀察到Instance是靜態屬性,因此我們可以跟成員屬性進行區分:
public override string ToString()
{
StringBuilder sb = new StringBuilder();
foreach (var prop in this.GetType().GetProperties(System.Reflection.BindingFlags.Public|System.Reflection.BindingFlags.Instance))
{
sb.AppendFormat("{0}:{1};",prop.Name,prop.GetValue(this,null));
}
return sb.ToString();
}
11 WPF使用MVVM模式,給界面的RatioButton賦值問題
如下:WPF界面
三個RatioButton在一個Groupbox中,它們是互斥的關係:
<GroupBox Grid.Column="0" Header="確認狀態">
<StackPanel>
<RadioButton Content="已確認" Margin="0,5,0,0"
IsChecked="{Binding IsAck,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<i:InvokeCommandAction Command="{Binding AckCheckedChangedCmd}" />
</i:EventTrigger>
<i:EventTrigger EventName="Unchecked">
<i:InvokeCommandAction Command="{Binding AckCheckedChangedCmd}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</RadioButton>
<RadioButton Content="未確認" Margin="0,5,0,0" IsChecked="{Binding IsUnack, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<i:InvokeCommandAction Command="{Binding AckCheckedChangedCmd}" />
</i:EventTrigger>
<i:EventTrigger EventName="Unchecked">
<i:InvokeCommandAction Command="{Binding AckCheckedChangedCmd}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</RadioButton>
<RadioButton Content="全部" Margin="0,5,0,0" IsChecked="{Binding IsAckAndUnack,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<i:InvokeCommandAction Command="{Binding AckCheckedChangedCmd}" />
</i:EventTrigger>
<i:EventTrigger EventName="Unchecked">
<i:InvokeCommandAction Command="{Binding AckCheckedChangedCmd}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</RadioButton>
</StackPanel>
</GroupBox>
現在我們想讓程序啓動時,自動選中第2個RatioButton,也就是這三個變量的值是互斥的,同一時刻只能有一個爲true,我只想賦值一個變量就想達到這個效果:
IsUnack=true;
如果把這一句加入到ViewModel的構造函數中,則不一定會起作用,因爲其他兩個變量IsAck和IsAckAndUnack的初始值也爲true,當ViewModel賦值到View的DataContext上時,由於界面互斥的關係,IsUnack
/IsAck
會被自動賦值爲false,IsAckAndUnack
保持爲true,因爲IsAckAndUnack綁定的控件在最後面,爲了解決這個問題,我們只有在窗體加載完成後再賦值IsUnack=true
.由於我們使用的是MVVM模式,我們將窗體的加載函數綁定到命令,在命令的處理函數中執行IsUnack=true:
MainWindow.xaml:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding WindowLoadCmd}"></i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
MainWindowViewModel:
private bool isAck=true;
public bool IsAck
{
get { return isAck; }
set { isAck = value;RaisePropertyChanged(nameof(IsAck)); }
}
private bool isUnack=true;
public bool IsUnack
{
get { return isUnack; }
set { isUnack = value;RaisePropertyChanged(nameof(IsUnack)); }
}
private bool isAckAndUnack = true;
public bool IsAckAndUnack
{
get { return isAckAndUnack; }
set { isAckAndUnack = value;RaisePropertyChanged(nameof(IsAckAndUnack)); }
}
public DelegateCommand WindowLoadCmd { get; set; }
public MainWindowViewModel()
{
WindowLoadCmd = new DelegateCommand(WindowLoad);
}
private void WindowLoad()
{
IsUnack=true;
}