diff --git a/README.md b/README.md index a5d8330..5d82374 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ # TYLDDB-C# C# version. + +A strongly typed constrained distributed file system that can act as a database. diff --git a/TYLDDB-CSharp.sln b/TYLDDB-CSharp.sln index 7d2180e..9ed67ba 100644 --- a/TYLDDB-CSharp.sln +++ b/TYLDDB-CSharp.sln @@ -12,6 +12,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TYLDDB.Test", "TYLDDB.Test\TYLDDB.Test.csproj", "{FDFD566C-CB13-43EB-970E-0EC0A21E36C0}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TYLDDB.Utils.FastCache.Test", "TYLDDB.Utils.FastCache.Test\TYLDDB.Utils.FastCache.Test.csproj", "{A58C0A6B-9BD4-4B29-9B5F-5FDFB849D23B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{95BC8FAF-AB03-44A3-A7E7-8E42FA7138E3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -26,10 +30,18 @@ Global {FDFD566C-CB13-43EB-970E-0EC0A21E36C0}.Debug|Any CPU.Build.0 = Debug|Any CPU {FDFD566C-CB13-43EB-970E-0EC0A21E36C0}.Release|Any CPU.ActiveCfg = Release|Any CPU {FDFD566C-CB13-43EB-970E-0EC0A21E36C0}.Release|Any CPU.Build.0 = Release|Any CPU + {A58C0A6B-9BD4-4B29-9B5F-5FDFB849D23B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A58C0A6B-9BD4-4B29-9B5F-5FDFB849D23B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A58C0A6B-9BD4-4B29-9B5F-5FDFB849D23B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A58C0A6B-9BD4-4B29-9B5F-5FDFB849D23B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {FDFD566C-CB13-43EB-970E-0EC0A21E36C0} = {95BC8FAF-AB03-44A3-A7E7-8E42FA7138E3} + {A58C0A6B-9BD4-4B29-9B5F-5FDFB849D23B} = {95BC8FAF-AB03-44A3-A7E7-8E42FA7138E3} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {19390089-354C-41AD-AF7D-9FB3998A88EA} EndGlobalSection diff --git a/TYLDDB.Test/Program.cs b/TYLDDB.Test/Program.cs index 942e036..b4583a7 100644 --- a/TYLDDB.Test/Program.cs +++ b/TYLDDB.Test/Program.cs @@ -1,19 +1,21 @@ using TYLDDB; using TYLDDB.Test; +string dbFilePath = "./example.lddb"; + ///////////////////////////////////////////////////////////////////////////////////////////////////////// 实例化 LDDB lddb = new LDDB(); ///////////////////////////////////////////////////////////////////////////////////////////////////////// 读取文件 -HighPrecisionTimer readDbTimer = new(); // 从发起读取文件指令到成功读取的总时间 +HighPrecisionTimer readDbTimer = new(); // 从发起读取文件到成功读取的总时间 +lddb.FilePath = dbFilePath; readDbTimer.Start(); -lddb.FilePath = "./example.lddb"; lddb.ReadingFile(); readDbTimer.Stop(); WriteTime("从发起读取文件指令到成功读取的总时间为: ", readDbTimer.ElapsedMilliseconds()); ///////////////////////////////////////////////////////////////////////////////////////////////////////// 读取数据库 -HighPrecisionTimer loadDbTimer = new(); // 从发起读取数据库指令到成功返回读取内容的总时间 +HighPrecisionTimer loadDbTimer = new(); // 从发起读取数据库到成功返回读取内容的总时间 loadDbTimer.Start(); lddb.LoadDatabase("database1"); Console.WriteLine(lddb.GetLoadingDatabaseContent()); // 输出database1内容 @@ -21,7 +23,7 @@ WriteTime("从发起读取数据库指令到成功返回读取内容的总时间为: ", loadDbTimer.ElapsedMilliseconds()); ///////////////////////////////////////////////////////////////////////////////////////////////////////// 获取所有数据库名称 -HighPrecisionTimer readAllDbNameTimer = new(); // 从发起读取数据库指令到成功返回读取内容的总时间 +HighPrecisionTimer readAllDbNameTimer = new(); // 从发起读取数据库名称到成功返回读取内容的总时间 readAllDbNameTimer.Start(); lddb.ReadAllDatabaseName(); readAllDbNameTimer.Stop(); @@ -32,7 +34,7 @@ Console.WriteLine(dbName); } } -WriteTime("从发起读取数据库指令到成功返回读取内容的总时间为: ", readAllDbNameTimer.ElapsedMilliseconds()); +WriteTime("从发起读取数据库名称到成功返回读取内容的总时间为: ", readAllDbNameTimer.ElapsedMilliseconds()); @@ -50,9 +52,7 @@ Console.ReadLine(); - - - +////////////////////////////////////////////////////////////////////////////////////////////////////////// Test Method ///////////////////////////////////////////////////////////////////////////////////////////////////////// 工具 static void WriteTime(string what, double time) diff --git a/TYLDDB.Utils.FastCache.Test/HighPrecisionTimer.cs b/TYLDDB.Utils.FastCache.Test/HighPrecisionTimer.cs new file mode 100644 index 0000000..7856bfe --- /dev/null +++ b/TYLDDB.Utils.FastCache.Test/HighPrecisionTimer.cs @@ -0,0 +1,44 @@ +using System.Diagnostics; + +namespace TYLDDB.Utils.FastCache.Test +{ + internal class HighPrecisionTimer + { + private Stopwatch stopwatch; + + public HighPrecisionTimer() + { + stopwatch = new Stopwatch(); + } + + // 启动计时器 + public void Start() + { + stopwatch.Start(); + } + + // 停止计时器 + public void Stop() + { + stopwatch.Stop(); + } + + // 重置计时器 + public void Reset() + { + stopwatch.Reset(); + } + + // 获取已用时间(毫秒) + public double ElapsedMilliseconds() + { + return stopwatch.Elapsed.TotalMilliseconds; + } + + // 获取已用时间(秒) + public double ElapsedSeconds() + { + return stopwatch.Elapsed.TotalSeconds; + } + } +} diff --git a/TYLDDB.Utils.FastCache.Test/Program.cs b/TYLDDB.Utils.FastCache.Test/Program.cs new file mode 100644 index 0000000..da4dde3 --- /dev/null +++ b/TYLDDB.Utils.FastCache.Test/Program.cs @@ -0,0 +1,76 @@ +using TYLDDB.Utils.FastCache; +using TYLDDB.Utils.FastCache.Test; + +SemaphoreSlimDefault(); + +Console.ReadLine(); + +// 普通线程锁的数据库读写运行 +static async void SemaphoreSlimDefault() +{ + var setTime = new HighPrecisionTimer(); + var setAsyncTime = new HighPrecisionTimer(); + var getByKeyTime = new HighPrecisionTimer(); + var getByKeyAsyncTime = new HighPrecisionTimer(); + var getKeysByValueTime = new HighPrecisionTimer(); + var getKeysByValueAsyncTime = new HighPrecisionTimer(); + var getAllCacheTime = new HighPrecisionTimer(); + var getAllCacheAsyncTime = new HighPrecisionTimer(); + + var cache = new Cache(); + + setTime.Start(); + cache.Set("TESTKEY1", "TESTVALUE1"); + setTime.Stop(); + + setAsyncTime.Start(); + await cache.SetAsync("TESTKEY2", "TESTVALUE2"); + setAsyncTime.Stop(); + + getByKeyTime.Start(); + Console.WriteLine("TESTKEY1对应的值:" + cache.GetByKey("TESTKEY1")); + getByKeyTime.Stop(); + + getByKeyAsyncTime.Start(); + Console.WriteLine("TESTKEY2对应的值:" + await cache.GetByKeyAsync("TESTKEY2")); + getByKeyAsyncTime.Stop(); + + cache.Set("TESTKEY3", "TESTVALUE2"); + + Console.WriteLine("TESTVALUE2对应的所有键:"); + getKeysByValueTime.Start(); + Console.WriteLine(string.Join(", ", cache.GetKeysByValue("TESTVALUE2"))); + getKeysByValueTime.Stop(); + + Console.WriteLine("TESTVALUE2对应的所有键:"); + getKeysByValueAsyncTime.Start(); + Console.WriteLine(string.Join(", ", await cache.GetKeysByValueAsync("TESTVALUE2"))); + getKeysByValueAsyncTime.Stop(); + + // 获取并输出所有缓存项(同步方法) + getAllCacheTime.Start(); + var allCacheSync = cache.GetAllCache(); + Console.WriteLine("同步获取所有缓存项:"); + foreach (var kvp in allCacheSync) + { + Console.WriteLine($"{kvp.Key}: {kvp.Value}"); + } + getAllCacheTime.Stop(); + + // 获取并输出所有缓存项(异步方法) + getAllCacheAsyncTime.Start(); + var allCacheAsync = await cache.GetAllCacheAsync(); + Console.WriteLine("异步获取所有缓存项:"); + foreach (var kvp in allCacheAsync) + { + Console.WriteLine($"{kvp.Key}: {kvp.Value}"); + } + getAllCacheAsyncTime.Stop(); + + + Console.WriteLine("时间消耗:"); + Console.WriteLine($"设置键值(同步):{setTime.ElapsedMilliseconds()}ms 设置键值(异步):{setAsyncTime.ElapsedMilliseconds()}ms"); + Console.WriteLine($"根据键获取值(同步):{getByKeyTime.ElapsedMilliseconds()}ms 根据键获取值(异步):{getByKeyAsyncTime.ElapsedMilliseconds()}ms"); + Console.WriteLine($"根据值获取键(同步):{getKeysByValueTime.ElapsedMilliseconds()}ms 根据值获取键(异步):{getKeysByValueAsyncTime.ElapsedMilliseconds()}ms"); + Console.WriteLine($"获取所有缓存项(同步):{getAllCacheTime.ElapsedMilliseconds()}ms 获取所有缓存项(异步):{getAllCacheAsyncTime.ElapsedMilliseconds()}ms"); +} diff --git a/TYLDDB.Utils.FastCache.Test/TYLDDB.Utils.FastCache.Test.csproj b/TYLDDB.Utils.FastCache.Test/TYLDDB.Utils.FastCache.Test.csproj new file mode 100644 index 0000000..bb48fc4 --- /dev/null +++ b/TYLDDB.Utils.FastCache.Test/TYLDDB.Utils.FastCache.Test.csproj @@ -0,0 +1,14 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + diff --git a/TYLDDB/Basic/Exception.cs b/TYLDDB/Basic/Exception.cs index 5a20261..c354060 100644 --- a/TYLDDB/Basic/Exception.cs +++ b/TYLDDB/Basic/Exception.cs @@ -2,48 +2,6 @@ namespace TYLDDB.Basic { -#if NET8_0_OR_GREATER - /// - /// The main error module, most of them use this
- /// 主要的错误模块,大部分都使用这个 - ///
- public class TylddbException(string message) : Exception(message) { } - /// - /// File opening failure
- /// 文件打开失败 - ///
- public class FileOpenFailException(string message) : TylddbException(message) { } - /// - /// File read failure
- /// 文件读取失败 - ///
- public class FileReadingFailException(string message) : TylddbException(message) { } - /// - /// File not found
- /// 文件未找到 - ///
- public class FileNotFoundException(string message) : TylddbException(message) { } - /// - /// The file path is null or space
- /// 文件路径为null或空白 - ///
- public class FilePathIsNullOrWhiteSpace(string message) : TylddbException(message) { } - /// - /// Incorrect or invalid data
- /// 错误或无效的数据 - ///
- public class InvalidDataException(string message) : TylddbException(message) { } - /// - /// Database not found
- /// 未找到数据库 - ///
- public class DatabaseNotFoundException(string message) : TylddbException(message) { } - /// - /// Description Failed to obtain the database content
- /// 数据库内容获取失败 - ///
- public class GetDatabaseContentErrorException(string message) : TylddbException(message) { } -#else /// /// The main error module, most of them use this
/// 主要的错误模块,大部分都使用这个 @@ -140,5 +98,4 @@ public class GetDatabaseContentErrorException : TylddbException ///
public GetDatabaseContentErrorException(string message) : base(message) { } } -#endif } diff --git a/TYLDDB/How-To-Use.md b/TYLDDB/How-To-Use.md new file mode 100644 index 0000000..ebee15e --- /dev/null +++ b/TYLDDB/How-To-Use.md @@ -0,0 +1,43 @@ +# How to use TYLDDB + +## Basic content + +### Read database + +#### Instantiate the LDDB class + +```c# +LDDB lddb = new LDDB(); +``` + +If you are using a higher version .NET, you can also write a little simpler. + +```c# +LDDB lddb = new(); +``` + +#### Read file + +```c# +lddb.FilePath = "./example.lddb"; +lddb.ReadingFile(); +``` + +The contents of the file are then loaded into memory. + +#### Select the database to read + +```c# +lddb.LoadDatabase("database1"); +``` + +This allows you to select the contents of the database for the next operation + +If you want to get all the contents of this database, you can use `lddb.GetLoadingDatabaseContent()` , which returns a string. + +#### Gets all database names + +```c# +lddb.ReadAllDatabaseName(); +``` + diff --git a/TYLDDB/TYLDDB.cs b/TYLDDB/TYLDDB.cs index a8c6986..0fe14ba 100644 --- a/TYLDDB/TYLDDB.cs +++ b/TYLDDB/TYLDDB.cs @@ -1,5 +1,5 @@ -using System.Collections.Generic; -using System.Threading.Tasks; +using System; +using System.Collections.Generic; using TYLDDB.Basic; using TYLDDB.Utils; @@ -10,19 +10,25 @@ namespace TYLDDB /// public class LDDB { - private string _filePath; // 私有字段存储文件路径 - private string _fileContent; // 私有字段存储文件内容 - private string _database; - private string _databaseContent; + /// + /// Instantiate the LDDB class
+ /// 实例化LDDB类 + ///
+ public LDDB() + { + // TODO + } + ///////////////////////////////////////////////////// 私有字段 + private string _filePath; // 存储文件路径 + private string _fileContent; // 存储文件内容 + private string _database; // 存储正在访问的数据库 + private string _databaseContent; // 存储数据库内容 private bool _isRead = false; // 是否已调用读取文件 - -#if NET6_0_OR_GREATER - private Database database = new(); -#else + private event Action OnFileReadComplete; private Database database = new Database(); -#endif + ///////////////////////////////////////////////////// 公开字段 /// /// Set the path where you want to read the file
/// 设置希望读取文件的路径 @@ -36,7 +42,6 @@ public string FilePath _filePath = value; // 只有通过验证后才设置值 } } - /// /// Names of all databases in the current file
/// 当前文件内所有数据库的名称 @@ -62,7 +67,7 @@ private static void ValidateFilePath(string path) ///
public void ReadingFile() { - _fileContent = ReadFile.ReadTylddbFile(FilePath); + _fileContent = Reader.ReadFile(FilePath); _isRead = true; } diff --git a/TYLDDB/TYLDDB.csproj b/TYLDDB/TYLDDB.csproj index 5b2f837..4a1776d 100644 --- a/TYLDDB/TYLDDB.csproj +++ b/TYLDDB/TYLDDB.csproj @@ -1,10 +1,10 @@  - netstandard1.6;netstandard2.0;netstandard2.1;net6.0;net8.0;net9.0 + netstandard2.0;netstandard2.1;net6.0;net8.0;net9.0 True TTYPDB.NET - 1.0.0-beta.1 + 1.0.0-alpha.1 QingYi-Studio LICENSE True @@ -20,10 +20,12 @@ + + True \ @@ -37,4 +39,8 @@ + + + + diff --git a/TYLDDB/Utils/FastCache/Cache.cs b/TYLDDB/Utils/FastCache/Cache.cs new file mode 100644 index 0000000..3551198 --- /dev/null +++ b/TYLDDB/Utils/FastCache/Cache.cs @@ -0,0 +1,286 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace TYLDDB.Utils.FastCache +{ + /// + /// Use cached key-value pairs for fast reads and writes.
+ /// 使用缓存的键值对来快速读写。 + ///
+ public class Cache + { + private readonly Dictionary keyValueDict; // 存储键->值映射 + private readonly Dictionary> valueKeyDict; // 存储值->键的映射 + private readonly SemaphoreSlim semaphore; // 控制并发访问 + + /// + /// Use cached key-value pairs for fast reads and writes.
+ /// 使用缓存的键值对来快速读写。 + ///
+ public Cache() + { + keyValueDict = new Dictionary(); + valueKeyDict = new Dictionary>(); + semaphore = new SemaphoreSlim(1, 1); // 使用信号量来同步 + } + + /// + /// Synchronization method: Obtain the corresponding value by key.
+ /// 同步方法:根据键获取对应的值。 + ///
+ /// Key
键 + /// Value
+ public string GetByKey(string key) + { + lock (semaphore) + { + keyValueDict.TryGetValue(key, out var value); + return value; + } + } + + /// + /// Asynchronous method: Obtains the corresponding value based on the key.
+ /// 异步方法:根据键获取对应的值。 + ///
+ /// Key
键 + /// Value
+ public async Task GetByKeyAsync(string key) + { + await semaphore.WaitAsync(); + try + { + keyValueDict.TryGetValue(key, out var value); + return value; + } + finally + { + semaphore.Release(); + } + } + + /// + /// Synchronization method: Obtains one or more keys according to the value.
+ /// 同步方法:根据值获取对应的一个或多个键。 + ///
+ /// Value
值 + /// Key (List)
键 (List)
+ public List GetKeysByValue(string value) + { + lock (semaphore) + { + if (valueKeyDict.ContainsKey(value)) + { + return valueKeyDict[value].ToList(); + } + return new List(); + } + } + + /// + /// Asynchronous method: Get one or more keys based on the value.
+ /// 异步方法:根据值获取对应的一个或多个键。 + ///
+ /// Value
值 + /// Key (List)
键 (List)
+ public async Task> GetKeysByValueAsync(string value) + { + await semaphore.WaitAsync(); + try + { + if (valueKeyDict.ContainsKey(value)) + { + return valueKeyDict[value].ToList(); + } + return new List(); + } + finally + { + semaphore.Release(); + } + } + + /// + /// 同步方法:设置一个键值对。 + /// + /// + /// + /// + public bool Set(string key, string value) + { + lock (semaphore) + { + if (keyValueDict.ContainsKey(key)) + { + return false; // 键已存在,不允许重复的键 + } + + keyValueDict[key] = value; + + if (!valueKeyDict.ContainsKey(value)) + { + valueKeyDict[value] = new HashSet(); + } + valueKeyDict[value].Add(key); + return true; + } + } + + /// + /// 异步方法:设置一个键值对。 + /// + /// + /// + /// + public async Task SetAsync(string key, string value) + { + await semaphore.WaitAsync(); + try + { + if (keyValueDict.ContainsKey(key)) + { + return false; // 键已存在,不允许重复的键 + } + + keyValueDict[key] = value; + + if (!valueKeyDict.ContainsKey(value)) + { + valueKeyDict[value] = new HashSet(); + } + valueKeyDict[value].Add(key); + return true; + } + finally + { + semaphore.Release(); + } + } + + /// + /// 同步方法:移除一个键值对。 + /// + /// + /// + public bool RemoveByKey(string key) + { + lock (semaphore) + { + if (keyValueDict.ContainsKey(key)) + { + var value = keyValueDict[key]; + keyValueDict.Remove(key); + + if (valueKeyDict.ContainsKey(value)) + { + valueKeyDict[value].Remove(key); + if (valueKeyDict[value].Count == 0) + { + valueKeyDict.Remove(value); + } + } + return true; + } + return false; + } + } + + /// + /// 异步方法:移除一个键值对。 + /// + /// + /// + public async Task RemoveByKeyAsync(string key) + { + await semaphore.WaitAsync(); + try + { + if (keyValueDict.ContainsKey(key)) + { + var value = keyValueDict[key]; + keyValueDict.Remove(key); + + if (valueKeyDict.ContainsKey(value)) + { + valueKeyDict[value].Remove(key); + if (valueKeyDict[value].Count == 0) + { + valueKeyDict.Remove(value); + } + } + return true; + } + return false; + } + finally + { + semaphore.Release(); + } + } + + /// + /// 同步方法:清空缓存。 + /// + public void Clear() + { + lock (semaphore) + { + keyValueDict.Clear(); + valueKeyDict.Clear(); + } + } + + /// + /// 异步方法:清空缓存。 + /// + /// + public async Task ClearAsync() + { + await semaphore.WaitAsync(); + try + { + keyValueDict.Clear(); + valueKeyDict.Clear(); + } + finally + { + semaphore.Release(); + } + } + + /// + /// Gets all key-value pairs.
+ /// 获取所有的键值对。 + ///
+ /// Key-value pair
键值对
+ public Dictionary GetAllCache() + { + lock (semaphore) + { + // 返回完整的键值对字典 + return new Dictionary(keyValueDict); + } + } + + /// + /// Gets all key-value pairs.
+ /// 获取所有的键值对。 + ///
+ /// Key-value pair
键值对
+ public async Task> GetAllCacheAsync() + { + await semaphore.WaitAsync(); + try + { + // 返回完整的键值对字典 + return new Dictionary(keyValueDict); + } + finally + { + semaphore.Release(); + } + } + } +} diff --git a/TYLDDB/Utils/ReadFile.cs b/TYLDDB/Utils/ReadFile.cs deleted file mode 100644 index ebd4d55..0000000 --- a/TYLDDB/Utils/ReadFile.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.IO; -using TYLDDB.Basic; - -namespace TYLDDB.Utils -{ - internal class ReadFile - { - public static string ReadTylddbFile(string filePath) - { - try - { - using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) - using (StreamReader reader = new StreamReader(fs)) - { - string content = reader.ReadToEnd(); - return content; - } - } - catch (TylddbException ex) - { - throw new FileReadingFailException($"Error reading file: {ex.Message}"); - } - } - } -} diff --git a/TYLDDB/Utils/Reader.cs b/TYLDDB/Utils/Reader.cs new file mode 100644 index 0000000..68e9949 --- /dev/null +++ b/TYLDDB/Utils/Reader.cs @@ -0,0 +1,40 @@ +using System.IO; +using System.Text; + +namespace TYLDDB.Utils +{ + /// + /// A struct used to read a file
+ /// 用于读取文件的结构体 + ///
+ public struct Reader + { + /// + /// Read file.(64KB buffer)
+ /// 读取文件。(64KB缓冲区) + ///
+ /// File path
文件路径 + /// File content
文件内容
+ public static string ReadFile(string filePath) + { + // 这里采用分块读取文件的方式,避免一次性加载大文件 + const int bufferSize = 65536; // 64 KB缓冲区 + byte[] buffer = new byte[bufferSize]; + StringBuilder fileContent = new StringBuilder(); + + using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) + { + int bytesRead; + while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0) + { + // 将每一块数据转换成字符串并累积到 fileContent 中 + string chunkContent = Encoding.UTF8.GetString(buffer, 0, bytesRead); + fileContent.Append(chunkContent); + } + } + + // 返回文件的全部内容 + return fileContent.ToString(); + } + } +} diff --git a/TYLDDB/Utils/Writer.cs b/TYLDDB/Utils/Writer.cs new file mode 100644 index 0000000..75e8674 --- /dev/null +++ b/TYLDDB/Utils/Writer.cs @@ -0,0 +1,10 @@ +namespace TYLDDB.Utils +{ + /// + /// A class used to write contents into the file
+ /// 用于写入文件的类 + ///
+ public class Writer + { + } +}