C#写代码踩坑记录(持续更新,欢迎大家把自己的坑留下,避免后面的人踩)

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,写代码注意啊,兄弟们

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值