using System;
using System.IO;
using OfficeOpenXml;
public class Example
{
public static void Main()
{
// 假设有一个名为 "example.xlsx" 的 Excel 文件
string filePath = "example.xlsx";
FileInfo fileInfo = new FileInfo(filePath);
// 使用 using 语句块来创建 ExcelPackage 对象,并确保结束时自动释放资源
using (ExcelPackage excelPackage = new ExcelPackage(fileInfo))
{
// 在此处使用 excelPackage 对象进行 Excel 文件读取操作
// 例如读取特定单元格的数据等
ExcelWorksheet worksheet = excelPackage.Workbook.Worksheets[0]; // 获取第一个工作表
object cellValue = worksheet.Cells["A1"].Value; // 读取 A1 单元格的数据
Console.WriteLine("A1 单元格的值为: " + cellValue);
} // 在此处结束后,excelPackage 对象会被自动释放资源
}
}
注意:代码中的`ExcelPackage`来自 `OfficeOpenXml` 库,它用于读取和写入 Excel 文件,特别是在处理 Office 2007 及以上版本的 `xlsx` 格式时非常方便。确保在项目中添加对 `EPPlus` 的引用,以使上述代码正常运行。同时,`ExcelPackage`类实现了 `IDisposable` 接口,因此最好在使用完毕后放在 `using` 语句块中,以确保资源能够正确释放。
using UnityEngine;
using UnityEditor;
using OfficeOpenXml;
using System.IO;
using System;
using System.Reflection;
[InitializeOnLoad]//启动时执行
public class Startup
{
static Startup()
{
string path = Application.dataPath + "/Editor/关卡管理.xlsx";//获取表格文件路径
string assetName = "Level";
FileInfo fileInfo = new FileInfo(path);//根据获取的路径获得表格文件
// LevelData levelData = new LevelData();
LevelData levelData = (LevelData)ScriptableObject.CreateInstance(typeof(LevelData));
using (ExcelPackage excelPackage = new ExcelPackage(fileInfo))
{
ExcelWorksheet worksheet = excelPackage.Workbook.Worksheets["僵尸"];
//遍历每一行
for (int i = worksheet.Dimension.Start.Row + 2; i <= worksheet.Dimension.End.Row; i++)
{
LevelItem levelItem = new LevelItem();//给每一行创建一个LevelItem对象
Type type = typeof(LevelItem);//获取 LevelItem 的类型
//遍历每一列
for (int j = worksheet.Dimension.Start.Column; j <= worksheet.Dimension.End.Column; j++)
{
//反射
FieldInfo variable = type.GetField(worksheet.GetValue(2, j).ToString());
string tableValue = worksheet.GetValue(i, j).ToString();
variable.SetValue(levelItem, Convert.ChangeType(tableValue, variable.FieldType));
}
//把每行的levelItem对象添加到LevelDataList列表中
levelData.LevelDataList.Add(levelItem);
}
}
AssetDatabase.CreateAsset(levelData, "Assets/Resources/" + assetName + ".asset");
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
}
ExcelPackage.Workbook.Worksheets
`ExcelPackage.Workbook.Worksheets` 是 EPPlus 库中的一个属性,用于获取 ExcelPackage 对象中的所有工作表(Worksheet)。
在 EPPlus 库中,`ExcelPackage` 是用于读取和写入 Excel 文件的主要类。`ExcelPackage` 对象代表了一个 Excel 文件,它可以包含一个或多个工作表(Worksheet)。
`ExcelPackage.Workbook` 是 ExcelPackage 对象的一个属性,用于获取 Excel 文件的工作簿(Workbook)。工作簿是一个包含所有工作表的容器,你可以在其中添加、删除和管理工作表。
`ExcelPackage.Workbook.Worksheets` 是工作簿的属性,用于获取工作簿中的所有工作表。它返回一个 `ExcelWorksheets` 对象,该对象是一个工作表集合,可以包含一个或多个工作表。
示例代码:
using System;
using OfficeOpenXml;
public class Example
{
public static void Main()
{
// 假设有一个名为 "example.xlsx" 的 Excel 文件
string filePath = "example.xlsx";
var fileInfo = new System.IO.FileInfo(filePath);
using (ExcelPackage excelPackage = new ExcelPackage(fileInfo))
{
// 获取工作簿
var workbook = excelPackage.Workbook;
// 获取工作簿中的所有工作表
var worksheets = workbook.Worksheets;
// 遍历所有工作表并输出它们的名称
foreach (var worksheet in worksheets)
{
Console.WriteLine("工作表名称: " + worksheet.Name);
}
}
}
}
在上面的示例中,我们打开名为 "example.xlsx" 的 Excel 文件,并使用 `ExcelPackage` 对象获取了工作簿。然后,我们使用 `workbook.Worksheets` 属性获取了工作簿中的所有工作表,并通过遍历输出了每个工作表的名称。
对于“反射”的理解:
1. `LevelItem` 类并没有直接提供用于获取字段的方法,而是通过 `Type` 类提供的反射方法来实现获取字段信息。
2. `Type` 类本身并没有获取字段的方法。它提供了一系列的方法,如 `GetField`、`GetFields`、`GetProperties` 等,来通过名称或条件来获取类型的字段、属性等信息。
因此,使用 `Type` 类的主要目的是为了实现反射,以在运行时动态地获取类型的成员(如字段、属性、方法等)信息。通过 `typeof(LevelItem)` 可以获取 `LevelItem` 类的 `Type` 对象,然后通过这个对象的反射方法 `GetField` 来获取 `LevelItem` 类中特定字段的信息。
`LevelItem` 类中没有直接获取字段的方法,因此我们使用 `typeof(LevelItem)` 来获取 `LevelItem` 类的 `Type` 对象,并通过这个对象来获取字段信息。反射机制让我们可以在运行时通过字符串名称来查找并访问类的成员,从而实现更加灵活和动态的编程。
`FieldInfo` 是反射中的一个类,它用于表示类的字段(Field)的信息,包括字段的名称、数据类型、访问修饰符等。通过 `FieldInfo` 类,我们可以在运行时获取和操作类的字段。
在 C# 中,字段(Field)是类或结构体中的成员变量,它用于存储对象的数据。字段可以是公共的、私有的或受保护的,可以包含不同的数据类型,比如整数、字符串、对象等。
通过反射,我们可以在运行时获取类的字段信息,包括字段的名称、数据类型以及其他修饰符(例如 public、private、static 等),并且可以通过反射设置或获取字段的值。
`FieldInfo` 类提供了一系列的属性和方法,用于获取和操作字段的信息。一些常用的属性和方法包括:
- `Name`:表示字段的名称。
- `FieldType`:表示字段的数据类型。
- `GetValue(object obj)`:获取指定对象中字段的值。
- `SetValue(object obj, object value)`:设置指定对象中字段的值。
示例:
假设有一个类 `Person`:
public class Person
{
public string Name;
public int Age;
}
我们可以通过反射获取 `Person` 类的字段信息:
Type type = typeof(Person);
// 获取 Name 字段的信息
FieldInfo nameField = type.GetField("Name");
Console.WriteLine("Name 字段的名称:" + nameField.Name);
Console.WriteLine("Name 字段的数据类型:" + nameField.FieldType);
// 获取 Age 字段的信息
FieldInfo ageField = type.GetField("Age");
Console.WriteLine("Age 字段的名称:" + ageField.Name);
Console.WriteLine("Age 字段的数据类型:" + ageField.FieldType);
通过 `FieldInfo`,我们可以获取字段的名称和数据类型,并进行相应的操作。反射机制使得我们在运行时可以动态地获取类的成员信息,为编程提供了更大的灵活性。
variable.SetValue(levelItem, Convert.ChangeType(tableValue, variable.FieldType)); // 使用反射设置字段的值,这句的用法
这句代码是使用反射机制设置 `LevelItem` 类的字段值的操作。
让我们逐步解释这个代码:
1. `variable.SetValue(levelItem, value);`
这行代码使用 `FieldInfo` 对象 `variable` 的 `SetValue` 方法来设置 `LevelItem` 类的字段值。
2. `levelItem` 是 `LevelItem` 类的一个实例,它是一个对象。
3. `value` 是要设置到字段的值,实际上在这个代码片段中是通过 `Convert.ChangeType(tableValue, variable.FieldType)` 表达式得到的。它是从 Excel 表格中读取的原始数据,并根据字段的类型进行类型转换,使其适配字段的数据类型。
所以,这段代码的作用是将 `tableValue` 转换成合适的类型,然后将这个值设置到 `LevelItem` 类的字段中。实际上,它相当于在运行时动态地设置了 `LevelItem` 类的字段值,而字段的名称是通过反射机制获取到的,因为在代码中我们并不知道具体的字段名称和类型,这些都是在运行时动态决定的。
示例:
假设 `LevelItem` 类中有一个字段 `Name` 是字符串类型,我们从 Excel 表格中读取了 `tableValue` 并为其赋值为 "John Doe"。现在我们想要将这个值设置到 `LevelItem` 类的 `Name` 字段上。
// 获取 Name 字段的信息
FieldInfo variable = type.GetField("Name");
// 假设 tableValue 的值为 "John Doe"
string tableValue = "John Doe";
// 将 tableValue 转换成合适的类型(字符串),然后设置到 Name 字段上
variable.SetValue(levelItem, Convert.ChangeType(tableValue, variable.FieldType));
现在 `levelItem.Name` 的值就是 "John Doe",我们成功将 Excel 表格中的数据设置到了 `LevelItem` 类的字段中。这是反射机制的强大之处,它允许我们在运行时根据不同的条件来访问和操作类的成员,从而实现更加灵活和动态的编程。
`SetValue()` 是 `FieldInfo` 类的一个方法,用于在运行时动态地设置对象的字段值。它允许我们通过反射机制在没有直接访问字段的情况下,通过字段名称来设置类的实例中特定字段的值。
方法签名如下:
public void SetValue(object obj, object value);
- `obj`:表示要设置字段值的对象实例。
- `value`:表示要设置的字段值,必须是一个 `object` 类型的对象。
使用 `SetValue()` 方法时,`obj` 参数表示类的一个实例,也就是要设置字段值的对象。`value` 参数是要设置到字段的具体值。(我认为:使用这个方法的FiledInfo类的variable对象,就代表obj的某个字段。)
需要注意的是,`value` 的类型必须与字段的类型兼容,否则在运行时可能会抛出异常。因此,在使用 `SetValue()` 方法设置字段值之前,我们需要确保将 `value` 转换为与字段类型相匹配的类型。
在之前的示例代码中:
FieldInfo variable = type.GetField(worksheet.GetValue(2, j).ToString());
string tableValue = worksheet.GetValue(i, j).ToString();
variable.SetValue(levelItem, Convert.ChangeType(tableValue, variable.FieldType));
- `variable` 是一个 `FieldInfo` 对象,表示 `LevelItem` 类中的一个字段。
- `tableValue` 是从 Excel 表格中读取的原始数据,它是一个字符串类型。
- `variable.SetValue(levelItem, Convert.ChangeType(tableValue, variable.FieldType))` 这行代码将 `tableValue` 转换为与字段类型相匹配的类型,然后通过反射将这个值设置到 `levelItem` 对象中字段 `variable` 上。
总结:`SetValue()` 方法是通过反射在运行时动态设置对象的字段值的机制。通过提供对象实例和字段值,我们可以在没有直接访问字段的情况下,通过字段名称来设置类的实例中特定字段的值。
AssetDatabase.CreateAsset(levelData, "Assets/Resources/" + assetName + ".asset");
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
这段代码将整个ScriptableObject一>LevelData保存成一个".Assets"文件,这样Unity在运行是就能够读取到这份数据了,对文件进行保存,刷新。