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;
}
12.WCF死锁的问题
具体现象见:https://social.microsoft.com/Forums/he-IL/1933a906-b5b1-4f40-a56b-67b7f6de4ca3/2085120110wcf22238358433622926102303403838239064?forum=visualcshartzhchs
当在客户端调用服务端的方法里,去使用回调时,客户端会卡住的问题,解决办法,在客户端实现的回调类上加这个标注:
[CallbackBehavior(UseSynchronizationContext = false)]
13.WPF 报 “无法使用 DependencyObject,它属于其父 Freezable 之外的其他线程”的错误
出现这样错误的原因是SolidColorBrush不能在主线程之外的线程创建
参考文章:http://www.111com.net/net/173/117010.htm
14 在现场中调用App.Current.MainWindow报错:
报错的写法:
Task.Factory.StartNew(() =>
{
**var main1 = App.Current.MainWindow;**
var result= PendingBox.Show("this is pendingbox in thread", "提示",false, main1 );
for (int i = 0; i < 10; i++)
{
this.Dispatcher.Invoke(()=> result.UpdateMessage(i.ToString()));
Thread.Sleep(1000);
}
result.Close();
});
正确的写法:
var main = App.Current.MainWindow;
Task.Factory.StartNew(() =>
{
var result= PendingBox.Show("this is pendingbox in thread", "提示",false, main );
for (int i = 0; i < 10; i++)
{
this.Dispatcher.Invoke(()=> result.UpdateMessage(i.ToString()));
Thread.Sleep(1000);
}
result.Close();
});
15 .netcore web api报414 url太长的解决方案
使用.netcore编写的WebApi进行get查询操作时,报如下错误:
解决方法:
设置kestrel服务器的缓冲大小:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
webBuilder.UseUrls("http://*:10000");
webBuilder.UseKestrel(a =>
{
a.Limits.MaxRequestLineSize = 32 * 1024 * 10;
a.Limits.MaxRequestHeadersTotalSize = 32 * 1024 * 10;
});
})
.UseNLog();
将MaxRequestLineSize
和 MaxRequestHeadersTotalSize
设置为320KB,默认为32KB
Timespan需要注意的事情
请看如下代码:
static void Main(string[] args)
{
var ts=TimeSpan.FromHours(5000.123);
Console.WriteLine(ts.Hours);
Console.ReadKey();
}
输出的结果竟然是8!!!!
看看注释定义,小时数的定义竟然是-24~23,小时不能超过24么??
本来想把5000.123
小时换算成时分秒的,怎么办?正确的方式如下:
var ts=TimeSpan.FromHours(5000.123);
Console.WriteLine($"{((int)(ts.TotalHours)).ToString("00")}:{ts.Minutes.ToString("00")}:{ts.Seconds.ToString("00")}");
Console.ReadKey();
ToString("00")
仅仅是想把事件转成最少两位显示而已
20230512遇到的坑
这个坑让我排查一个堆栈溢出的问题排查了将近一下午的时间!!!
请看如下代码示例:
static void Main(string[] args)
{
var flag = false;
Func<bool> f1 = () => flag == true;
Func<bool> f2 = () => flag = true;
var a = 5;
Func<int> f3 = () => a;
Func<int> f4 = () => a=6;
Test(f1);
Test(f2);
Test(f1);
Console.WriteLine("=====");
Test(f3);
Test(f4);
Test(f3);
Console.ReadLine();
}
static void Test<T>(Func<T> f)
{
Console.WriteLine(f());
}
Func<bool> f2 = () => flag = true;
这一句,大家肯定想的是无法通过编译,貌似flag=true
不返回信息。但是它能通过编译!!!
仔细想想,有的老一辈程序员喜欢这样写代码:
a=b=c=d=100;
这样写的意思就是把变量a
/b
/c
/d
都赋值为100
那其实a=6
就返回a
的值(赋值之后的值返回),这是一个语法糖
那么这个坑是如何造成我排查一下午的堆栈溢出的呢?
贴出我的代码:使用wpf+prism写的遇到的坑:
public class MainWindowViewModel:BindableBase
{
public MainWindowViewModel()
{
this.PushCmd = new DelegateCommand(Push, () => this.IsError=false);
}
public DelegateCommand PushCmd { get; set; }
private bool isError;
public bool IsError
{
get { return isError; }
set
{
isError = value;
this.RaisePropertyChanged(nameof(IsError));
this.PushCmd?.RaiseCanExecuteChanged();
}
}
}
本来是:this.PushCmd = new DelegateCommand(Push, () => this.IsError==false);
错写为:this.PushCmd = new DelegateCommand(Push, () => this.IsError=false);
还能通过编译。这样就导致:
- wpf界面获取PushCmd是否能执行
- 执行
() => this.IsError=false
委托 - 委托中对
IsError
赋值false - IsError的set中刷新
PushCmd
是否能执行 - 执行
() => this.IsError=false
委托 - …
发现程序一直执行() => this.IsError=false
从而进入递归死循环导致堆栈溢出
IEnumerable.All()
var list = new List<bool>();
Console.WriteLine(list.All(x=>x));
大家不去翻资料,认为下面该输出什么?没错,是true
.
写代码的时候没注意,造成了一个bug
微软官方已经写明白了,如果集合为空,则会返回true,写代码注意啊,兄弟们