csv和表格的转换 二进制序列化 / 反序列化 窗体跳转和传值

1.csv和表格的转换

DataTable dt = new DataTable();

dataGridView1.DataSource = dt;

1 datagridview 控件的常用属性

dataGridView1.Columns 表格控件的所有列

dataGridView1.Columns .Count 表格控件的所有列的个数

dataGridView1.Rows 表格控件的所有行

dataGridView1[0,0] 根据几行几列取出表格数据

dataGridView1.Columns[i].HeaderTex 表格控件的列的文本内容

2 dataTable常用的属性和方法

DataTable dt = new DataTable(); 创建dataTable类型

DataRow row = dt.NewRow(); 创建一行

dt.Rows.Add(row); 向dataTable添加一行

dt.Columns.Add(); 向dataTable添加一列

dt.Columns datatable所有的列

dt.Rows datatable所有的行

dt.Columns[i].ColumnName;列标题

cs 复制代码
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            //dataGridView1.DataSource = 集合

            //DataTable 表格数据 分为列和行,需要给列添加数据,也需要给行添加数据
            //DataTable dt = new DataTable();
            //dt.Columns; // 列的集合
            //dt.NewRow();//创建一个新行 
            //dataGridView1.DataSource = dt;

        }

        //读取csv
        private void button1_Click(object sender, EventArgs e)
        {

            //CSVAPI c = new CSVAPI();
            //c.DuQu(dataGridView1);


            DataTable dt = new DataTable();// 创建一个表格数据
            OpenFileDialog ofd = new OpenFileDialog();
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                //获取文件路径进行读取  ofd.FileName
                FileStream fs = new FileStream(ofd.FileName, FileMode.Open, FileAccess.Read);
                StreamReader sr = new StreamReader(fs, Encoding.Default);
                string data; // 读取的数据
                string[] lines; // 使用逗号分割之后的数组
                bool isHead = true;//是否是表头数据
                //data=sr.ReadLine() 一行一行去读取
                //sr.ReadToEnd(); 读取完
                while ((data = sr.ReadLine()) != null) // 证明有数据
                {
                    //MessageBox.Show(data); //姓名,年龄,性别
                    lines = data.Split(',');//["姓名","年龄","性别"]
                    if (isHead)//如果是表头的
                    {
                        for (int i = 0; i < lines.Length; i++)
                        {
                            dt.Columns.Add(lines[i]); //给列添加数据
                        }
                        isHead = false; // 处理完表头之后把isHead=false
                    }
                    else  //不是表头数据 ["张三",10,"男"]
                    {
                        DataRow dataRow = dt.NewRow();//创建行 多个单元格 给每一个单元格赋值
                        for (int i = 0; i < lines.Length; i++)
                        {
                            // dataRow[i] 给每一个单元格赋值
                            dataRow[i] = lines[i];
                        }
                        dt.Rows.Add(dataRow); //添加到dt的Rows属性

                    }
                }
                //DataTable类型的数据  需要Columns 、Rows 赋值
                dataGridView1.DataSource = dt; //绑定数据源

            }

        }

        //写入csv
        private void button2_Click(object sender, EventArgs e)
        {
            //把dataGridView1表格展示的数据保存到csv
            //怎么从dataGridView1控件把数据取出来 赋值给DataTable ,再把DataTable 写入csv
            DataTable dt = new DataTable(); // 需要Columns 、Rows 赋值

            //从dataGridView1.Columns所有的列取出列表题
            for (int i = 0; i < dataGridView1.Columns.Count; i++)
            {
                dt.Columns.Add(dataGridView1.Columns[i].HeaderText);// 把控件的列的标题取出来赋值给datatable的列中
            }
            // MessageBox.Show(string.Join(",", dt.Columns[1])+""); Columns 已经存储了列表题

            //MessageBox.Show(dataGridView1.Rows.Count + "");
            //给dt的Rows赋值 add(DataRow)
            for (int i = 0; i < dataGridView1.Rows.Count; i++) //遍历控件的行数
            {
                DataRow dr = dt.NewRow(); //dr 每一个单元格赋值,取出每一个控件单元格值
                for (int j = 0; j < dataGridView1.Columns.Count; j++)
                {
                    ////dataGridView1[j, i].Value  访问第j列 第i行的单元格
                    dr[j] = dataGridView1[j, i].Value;
                    // MessageBox.Show(dataGridView1[j, i].Value.ToString());
                }
                dt.Rows.Add(dr);
            }

            //以上完成给DatatTable 类型填充数据了 开始写入csv
            SaveFileDialog dialog = new SaveFileDialog();   //保存对话框
            dialog.Filter = "CSV文件(*.csv)|*.csv|CSV文件(*.txt) |*.txt"; //过滤属性
            if (dialog.ShowDialog()==DialogResult.OK)
            {

                FileStream fs = new FileStream(dialog.FileName, FileMode.Create, FileAccess.Write);
                StreamWriter sr = new StreamWriter(fs, Encoding.Default);
                string data = "";//写入字符串

                //分表头写入
                for (int i = 0; i < dt.Columns.Count; i++)
                {
                    data += dt.Columns[i].ColumnName;
                   
                    if (i< dt.Columns.Count -1) //需要拼接逗号
                    {
                        data += ",";
                    }
                    
                }
                sr.WriteLine(data);

                data = null;// 防止重复写入
                for (int i = 0; i < dt.Rows.Count-1; i++) //从DataTable数据里面去取单元格数据
                {
                    for (int j = 0; j < dt.Columns.Count; j++)
                    {
                        data += dt.Rows[i][j];//dt.Rows[i] 第几行 ,dt.Rows[i][j] 第几行d第几个格子
                        if (j< dt.Columns.Count-1)
                        {
                            data += ",";
                        }
                    }
                    sr.WriteLine(data);
                    data = null;
                }
                sr.Close();
                fs.Close();
            }
        }
    }

2. 二进制序列化 / 反序列化的核心概念

  • 序列化 :将内存中的对象 转换为二进制数据流(字节序列),便于存储到文件、网络传输等;
  • 反序列化 :将二进制数据流还原为内存中的对象,恢复对象的属性和状态;
  • 与 JSON 序列化的区别:JSON 序列化是对象↔字符串(可读),二进制序列化是对象↔二进制(不可读、体积更小、效率更高)。
2. 关键类与特性
元素 作用
[Serializable] 标记类允许被序列化 ,缺少该特性会抛出SerializationException异常;
BinaryFormatter .NET 提供的二进制序列化工具,核心方法:- Serialize(流, 对象):序列化对象到流;- Deserialize(流):从流反序列化,返回object需强转;
FileStream 文件流,用于读写本地文件,是序列化的 "载体"(序列化需依托流完成);
3. 代码关键细节

csharp

运行

复制代码
// 序列化核心代码
FileStream fs = new FileStream("1.txt", FileMode.Create); // 创建/覆盖文件流
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(fs,i); // 把Info对象i序列化到文件流中
fs.Close(); // 必须关闭流,释放文件占用

// 反序列化核心代码
FileStream fs = new FileStream("1.txt", FileMode.Open); // 打开文件流
Info i1 =  binaryFormatter.Deserialize(fs) as Info; // 反序列化后强转为Info类型
  • 反序列化返回值是object类型,需通过as或强制类型转换((Info))转为目标类型;
  • 流操作后必须调用Close()(或用using自动释放),否则会导致文件被占用无法修改 / 删除。
4. 优化建议(新手必知)

原代码未处理异常且未优雅释放流,建议优化为:

csharp

运行

复制代码
// 序列化优化版(using自动释放流,try-catch处理异常)
private void button1_Click(object sender, EventArgs e)
{
    try
    {
        Info i = new Info() { Name = "中东小霸王", Description = "以色列三村一镇" };
        // using语句:代码块结束自动释放FileStream,无需手动Close
        using (FileStream fs = new FileStream("1.txt", FileMode.Create))
        {
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(fs, i);
        }
        MessageBox.Show("序列化成功!");
    }
    catch (Exception ex)
    {
        MessageBox.Show("序列化失败:" + ex.Message);
    }
}

// 反序列化优化版
private void button2_Click(object sender, EventArgs e)
{
    try
    {
        if (!File.Exists("1.txt")) // 先判断文件是否存在
        {
            MessageBox.Show("文件不存在!");
            return;
        }
        using (FileStream fs = new FileStream("1.txt", FileMode.Open))
        {
            BinaryFormatter binaryFormatter = new BinaryFormatter();
            Info i1 = binaryFormatter.Deserialize(fs) as Info;
            if (i1 != null) // 防止反序列化失败导致null
            {
                MessageBox.Show(i1.Name + "----" + i1.Description);
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show("反序列化失败:" + ex.Message);
    }
}
cs 复制代码
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            //  json序列化和反序列化 :  把对象和字符串之间转换过程
            // 二进制序列化和反序列化: 把对象和二进制字符串之间转换过程
        }
        //写入 把对象转成二进制字符 再写入本地
        private void button1_Click(object sender, EventArgs e)
        {
            Info i = new Info()
            {
                Name = "中东小霸王",
                Description = "以色列三村一镇"
            };


            FileStream fs = new FileStream("1.txt", FileMode.Create);
            //二进制格式化工具
            BinaryFormatter bf = new BinaryFormatter();

            bf.Serialize(fs,i);//二进制序列化 参数1:文件流 ,参数2序列化的对象

            fs.Close(); //关闭数据流
        }

        //读取
        private void button2_Click(object sender, EventArgs e)
        {
            FileStream fs = new FileStream("1.txt", FileMode.Open);
            BinaryFormatter binaryFormatter = new BinaryFormatter();
            Info i1 =  binaryFormatter.Deserialize(fs) as Info;// 反序列化
            MessageBox.Show(i1.Name + "----" + i1.Description);
            fs.Close();
        }
    }
    // 标记一个类 可以被序列化
    [Serializable]
    public class Info
    {
        public string Name { get; set; }
        public string Description { get; set; } //Description  描述
        
    }

3. 窗体跳转传值

创建并显示另一个窗体的核心语法:

csharp

运行

复制代码
// 1. 创建目标窗体对象(可传参)
Form2 f2 = new Form2(/* 可选:构造函数参数 */);
// 2. 显示窗体(两种方式)
f2.Show();          // 非模态显示
f2.ShowDialog();    // 模态显示
2. 两种窗体显示方式的核心区别
显示方式 特点 适用场景
Show() 非模态窗口,不阻塞 UI 线程;Form1 和 Form2 可同时操作;Form1 代码会继续向下执行; 需同时操作多个窗体(如主窗体 + 子窗体)
ShowDialog() 模态窗口,阻塞 UI 线程;必须关闭 Form2 后,Form1 的代码才会继续执行;Form2 置顶,无法操作 Form1; 需用户完成子窗体操作后再继续(如弹窗、确认框)
3. 窗体间传值的两种基础方式(重点)
方式 1:通过构造函数传参(推荐,数据初始化更规范)

实现步骤(需配合 Form2 的代码):

  • Step1:在 Form2 中定义带参数的构造函数,接收 Form1 传递的值: csharp

    运行

    复制代码
    // Form2中的代码
    public partial class Form2 : Form
    {
        private string _receiveInfo; // 存储接收的值
        // 自定义构造函数,接收Form1传递的参数
        public Form2(string info)
        {
            InitializeComponent();
            _receiveInfo = info; // 保存传递过来的值
        }
    }
  • Step2:Form1 中创建 Form2 时传参: csharp

    运行

    复制代码
    // Form1中的代码
    Form2 f2 = new Form2(info); // 把Form1的info传给Form2的构造函数
    f2.Show();
方式 2:通过公共属性赋值(灵活,可随时赋值)

实现步骤(需配合 Form2 的代码):

  • Step1:在 Form2 中定义公共属性,用于接收值: csharp

    运行

    复制代码
    // Form2中的代码
    public partial class Form2 : Form
    {
        // 定义公共属性,供Form1赋值
        public string s2 { get; set; } 
        
        private void Form2_Load(object sender, EventArgs e)
        {
            // 窗体加载时,可使用接收的s2值(如显示在控件上)
            MessageBox.Show("接收的值:" + s2);
        }
    }
  • Step2:Form1 中创建 Form2 后,给属性赋值: csharp

    运行

    复制代码
    // Form1中的代码
    Form2 f2 = new Form2(info); // 先传构造函数参数
    f2.s2 = s1; // 再给Form2的公共属性s2赋值
    f2.Show();
4. 代码注意点(新手易踩坑)
  • 原代码中Form2 f2 = new Form2(info); 要求 Form2 必须有带 string 参数的构造函数,否则会编译报错;
  • 公共属性传值需保证 Form2 的属性是public(如public string s2 { get; set; }),private 属性无法被 Form1 赋值;
  • 若使用ShowDialog(),Form2 关闭前,Form1 的后续代码不会执行(比如赋值、其他操作);
  • 窗体传值是单向的(Form1→Form2),若需 Form2 回传值给 Form1,需额外处理(如 Form2 定义事件、回调函数)。

三、核心知识点总结

  1. 窗体显示Show()是非模态(不阻塞),ShowDialog()是模态(阻塞),根据业务场景选择;
  2. 传值方式
    • 构造函数传参:适合初始化必传值,代码更规范;
    • 公共属性赋值:适合可选值、多值传递,更灵活;

From1代码

cs 复制代码
_05跳转传值
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        public string info = "催缴物业费"; // 演示构造函数传参数
        public string s1 = "code change world";
             
        private void button1_Click(object sender, EventArgs e)
        {
            //方法1 通过构造函数传参数进行传递
            // Form2 f2 = new Form2(info); //创建form的窗体对象
            //f2.Show();// 展示form2窗体  不会阻塞UI线程的
            //f2.ShowDialog(); // 以对话框的方式进行展示,阻塞UI界面, 必须用户关闭窗体之后才会往下执行


            //方法2: 通过在Form2定义一个公共属性接收传递的变量,在form2的Form2_Load事件可以接收传递的
            Form2 f2 = new Form2(info);
            f2.s2 = s1; //把s1传递给form2的s2属性
            f2.Show();
        }
    }

Form2代码

cs 复制代码
   public partial class Form2 : Form
   {
       public string s2 { get; set; }
       public Form2(string info)
       {
           InitializeComponent();
           MessageBox.Show(info + "------"+ s2);
       }

       //窗体的加载事件,当窗体里面控件了 资源加载完毕之后执行
       private void Form2_Load(object sender, EventArgs e)
       {
           MessageBox.Show( "+++++++" + s2);

       }
   }

2.通过方法传值

一. 预定义委托(Func/Action/Predicate)------ 核心重点

C# 提供了一系列预定义委托,无需手动定义委托类型就能直接使用,避免重复编写委托声明,代码更简洁。

委托类型 核心特征 示例绑定的方法
Action 无返回值,可重载(如Action<int>带 1 个 int 参数、Action<int,string>带 2 个参数) Action f2 = T2;(T2 无参无返回)
Func<TResult> 有返回值(最后一个泛型参数是返回值类型),可重载(如Func<int,string>:int 参数,string 返回) Func<string> f1 = T1;(无参,返回 string)Func<bool> f3 = T3;(无参,返回 bool)
Predicate<T> 专用委托:只有 1 个参数 ,返回值固定为bool(等价于Func<T,bool> Predicate<int> f4 = T4;(int 参
4. 新手易踩坑点
  • 委托变量未赋值时调用会抛出NullReferenceException,因此调用前需判空(if (f1 != null));
  • Form2 中接收委托 / 对象的变量必须是public,否则 Form1 无法访问赋值;
  • Predicate<T>只能绑定 "单参数、返回 bool" 的方法,不能绑定无参或多参数方法(如Predicate<int> f4 = T3;会编译报错,因为 T3 无参)。

二、核心知识点总结

  1. 预定义委托Action(无返回值)、Func<>(有返回值)、Predicate<T>(单参数 + bool 返回)是 C# 内置委托,无需手动定义即可绑定对应特征的方法;
  2. 委托本质:是方法的 "引用",可将方法赋值给委托变量,实现方法的传递(如窗体间传方法);
  3. 窗体传值拓展 :除了传递普通数据,还可通过委托传递方法、通过this传递窗体实例,实现窗体间的方法调用 / 对象交互。

From1

cs 复制代码
 public partial class Form1 : Form
 {
     
     public Form1()
     {
         InitializeComponent();
         Func<string> f1 = T1; //接收一个有返回值的方法
         Action f2 = T2; //接收一个无返回值的方法
         Func<bool> f3 = T3;//接收一个返回值为true的方法
         Predicate<int> f4 = T4; //接收一个返回值为true的方法
     }
     bool T4(int a)
     {
         return true;
     }
     bool T3()
     {
         return true;
     }
     void T2()
     {

     }
     public string T1()
     {
         return "Hello world";
     }
     private void button1_Click(object sender, EventArgs e)
     {
         Form2 f2 = new Form2();
         f2.f1 = T1;//把T1方法传递给form2窗体的f1变量
         f2.ffff = this; // 把窗体1this对象传递给form2变量
         f2.Show();

     }
 }

From2

cs 复制代码
 public partial class Form2 : Form
 {
     public Func<string> f1;
     public Form1 ffff; // 接收窗体1的对象
     public Form2()
     {
         InitializeComponent();
      
     }
     // Form2怎么找Form1对象
     private void Form2_Load(object sender, EventArgs e)
     {
         // f1?.Invoke()
         // f1? 表示f1是否有值
         //.Invoke()再去调用f1方法
         MessageBox.Show( f1?.Invoke()+"----" + ffff.T1());
     }
 }
相关推荐
WarPigs1 天前
数据库笔记
数据库·笔记
欢乐的小猪1 天前
windows10如何安装多个版本mysql
数据库·mysql
dishugj1 天前
oracle 监听常见报错解决
数据库·oracle
电商API_180079052471 天前
获取淘宝商品视频API教程:从授权到落地实战
大数据·数据库·人工智能·数据分析·音视频
工业甲酰苯胺1 天前
使用 C# 和 SQL Server 自动化邮件中的用户分配数据处理
数据库·c#·自动化
Antoine-zxt1 天前
MySQL数据脱敏技术全解析:从原理到可视化实践
数据库·mysql
DemonAvenger1 天前
深入Redis Stream:打造高效消息队列系统的实战指南
数据库·redis·性能优化
萧曵 丶1 天前
MySQL Undo/Redo Log详解
数据库·mysql
虎啊兄弟1 天前
RocketMQ面试题
数据库·rocketmq