• [转] C#编程实践


    作者:陈省

            最近一段时间学习使用C#编程,因为用惯了Delphi,发现C#类库还是不太完善(我用的是.Net Framework 1.0,不知道.Net Framework 1.1有哪些改进),此外Visual Studio 2002也有不完善的地方,不知道Visual Studio 2003有哪些改进呢。比如没有提供Ini文件的访问类,比如输入框不能像Delphi那样指定默认的输入法(更正:为了控制输入法,.NET类库在System.Windows.Forms.InputLanguage类中提供了支持),为此我不得不写了一个Ini访问类和根据输入法名称切换输入法的类。

    问题列表:

    C# Ini访问类
    C# 输入法切换类
    使用C#读写文件
    格式化字符串
    从Assemble中加载自定义资源
    对StringCollection进行排序
    C#Builder的Open Tools Api的Bug
    使用反射动态设定组件属性
    将字符串复制到剪贴板
    获取程序文件的版本信息
    利用反射动态加载Assembly动态执行类型方法
    其他问题

    C# Ini访问类

    using System;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Collections;
    using System.Collections.Specialized;
    using System.Windows.Forms;

    namespace SharpPlus.Ini {
    /// <summary>
    /// 一个模仿Delphi的TIniFile的类
    /// 修订:1.1 修正了对中文系统的支持。
    /// 1.2 增加了UpdateFile方法,实现了对Win9x的支持
    /// 1.3 增加了读写布尔,整数的操作
    /// 1.4 修正了写Ini虽然成功,但是会抛出异常的错误
    /// 1.5 ReadString返回的是Trim后的字符串
    /// 1.6 统一并扩大了读写缓冲区的大小
    /// </summary>
    public class IniFile {
    public string FileName; //INI文件名
    //声明读写INI文件的API函数
    [DllImport("kernel32")]
    private static extern bool WritePrivateProfileString(string section,string key,string val,string filePath);
    [DllImport("kernel32")]
    private static extern int GetPrivateProfileString(string section,string key,string def, byte[] retVal,int size,string filePath);
    //类的构造函数,传递INI文件名
    public IniFile(string AFileName) {
    // 判断文件是否存在
    FileInfo fileInfo=new FileInfo(AFileName);
    //Todo:搞清枚举的用法
    if ((!fileInfo.Exists)) //|| (FileAttributes.Directory in fileInfo.Attributes))
    throw(new ApplicationException("Ini文件不存在"));
    //必须是完全路径,不能是相对路径
    FileName = fileInfo.FullName;
    }
    //写INI文件
    public void WriteString(string Section,string Ident,string Value) {
    if (!WritePrivateProfileString(Section, Ident,Value,FileName))
    {
    // Todo:抛出自定义的异常
    throw(new ApplicationException("写Ini文件出错"));
    }
    }
    //读取INI文件指定
    public string ReadString(string Section,string Ident, string Default) {
    //StringBuilder Buffer = new StringBuilder(255);
    Byte[] Buffer=new Byte[65535];
    int bufLen=GetPrivateProfileString(Section,Ident,Default,Buffer, Buffer.GetUpperBound(0),FileName);
    //必须设定0(系统默认的代码页)的编码方式,否则无法支持中文
    string s=Encoding.GetEncoding(0).GetString(Buffer);
    s=s.Substring(0,bufLen);
    return s.Trim();
    }

    //读整数
    public int ReadInteger(string Section, string Ident , int Default){
    string intStr=ReadString(Section, Ident, Convert.ToString(Default));
    try{
    return Convert.ToInt32(intStr);
    }
    catch (Exception ex){
    Console.WriteLine(ex.Message);
    return Default;
    }
    }

    //写整数
    public void WriteInteger(string Section,string Ident, int Value){
    WriteString(Section, Ident, Value.ToString());
    }

    //读布尔
    public bool ReadBool(string Section, string Ident, bool Default){
    try
    {
    return Convert.ToBoolean(ReadString(Section, Ident, Convert.ToString(Default) ));
    }
    catch (Exception ex){
    Console.WriteLine(ex.Message);
    return Default;
    }
    }

    //写Bool
    public void WriteBool(string Section, string Ident , bool Value){
    WriteString(Section, Ident, Convert.ToString(Value));
    }

    //从Ini文件中,将指定的Section名称中的所有Ident添加到列表中
    public void ReadSection(string Section, StringCollection Idents) {
    Byte[] Buffer=new Byte[16384];
    //Idents.Clear();

    int bufLen=GetPrivateProfileString(Section, null, null, Buffer, Buffer.GetUpperBound(0),
    FileName);
    //对Section进行解析
    GetStringsFromBuffer(Buffer, bufLen, Idents);
    }

    private void GetStringsFromBuffer(Byte[] Buffer, int bufLen, StringCollection Strings) {
    Strings.Clear();
    if (bufLen!=0) {
    int start=0;
    for(int i=0; i < bufLen; i++) {
    if ((Buffer[i] == 0) && ((i-start)>0)) {
    String s=Encoding.GetEncoding(0).GetString(Buffer, start, i-start);
    Strings.Add(s);
    start=i+1;
    }
    }
    }
    }
    //从Ini文件中,读取所有的Sections的名称
    public void ReadSections(StringCollection SectionList) {
    //Note:必须得用Bytes来实现,StringBuilder只能取到第一个Section
    byte[] Buffer = new byte[65535];
    int bufLen=0;
    bufLen = GetPrivateProfileString(null, null, null,Buffer,
    Buffer.GetUpperBound(0), FileName);
    GetStringsFromBuffer(Buffer, bufLen, SectionList);
    }
    //读取指定的Section的所有Value到列表中
    public void ReadSectionValues(string Section, NameValueCollection Values) {
    StringCollection KeyList=new StringCollection();
    ReadSection(Section, KeyList);
    Values.Clear();
    foreach(string key in KeyList) {
    Values.Add(key, ReadString(Section, key, ""));
    }
    }
    //清除某个Section
    public void EraseSection(string Section) {
    //
    if (!WritePrivateProfileString(Section, null, null, FileName)) {
    throw(new ApplicationException("无法清除Ini文件中的Section"));
    }
    }
    //删除某个Section下的键
    public void DeleteKey(string Section, string Ident) {
    WritePrivateProfileString(Section, Ident, null, FileName);
    }
    //Note:对于Win9X,来说需要实现UpdateFile方法将缓冲中的数据写入文件
    //在Win NT, 2000和XP上,都是直接写文件,没有缓冲,所以,无须实现UpdateFile
    //执行完对Ini文件的修改之后,应该调用本方法更新缓冲区。
    public void UpdateFile() {
    WritePrivateProfileString(null, null, null, FileName);
    }

    //检查某个Section下的某个键值是否存在
    public bool ValueExists(string Section, string Ident) {
    //
    StringCollection Idents=new StringCollection();
    ReadSection(Section, Idents);
    return Idents.IndexOf(Ident)>-1;
    }

    //确保资源的释放
    ~IniFile(){
    UpdateFile();
    }
    }
    }

    C# 输入法切换类

    C#的编辑组件只有ImeMode属性,没有Delphi中组件的ImeName属性,下面的类可以用来根据ImeName设定当前系统的Ime。(更正:为了控制输入法,.NET类库在System.Windows.Forms.InputLanguage类中提供了支持。)

    using System;
    using System.Runtime.InteropServices;
    using System.Collections;
    using Microsoft.Win32;

    namespace Screen
    {
    /// <summary>
    /// Ime 的摘要说明。
    /// 实现本地化输入法
    /// 参考Delphi中的实现
    /// </summary>
    public class Ime
    {

    [DllImport("user32")]
        private static extern uint ActivateKeyboardLayout(uint hkl, uint Flags);
    [DllImport("user32")]
    private static extern uint LoadKeyboardLayout(string pwszKLID,uint Flags);
    [DllImport("user32")]
    private static extern uint GetKeyboardLayoutList(int nBuff, uint[] List);

    private static Hashtable FImes;
    public static uint KLF_ACTIVATE = 1;

    public Ime()
    {
    //
    // TODO: 在此处添加构造函数逻辑
    //
    }

    //设定当前Ime,使用方法Ime.SetImeName("中文 (简体) - 拼音加加");
    public static void SetImeName(string ImeName)
    {
    //字符串形式
    if (FImes==null)
    GetImes();
    uint id = Convert.ToUInt32(FImes[ImeName]);
    SetIme(id);
    }

    public static void SetIme(uint ImeId)
    {
    //Id样式
    if (ImeId>0)
    ActivateKeyboardLayout(ImeId, KLF_ACTIVATE);
    }

    //获得所有的Ime列表
    public static Hashtable GetImes()
    {
    if (FImes==null)
    FImes=new Hashtable();
    else
    return FImes;
    uint[] KbList=new uint[64];
    uint TotalKbLayout = GetKeyboardLayoutList(64, KbList);

    for (int i=0 ; i< TotalKbLayout ; i++)
    {
    string RegKey=String.Format("System\\CurrentControlSet\\Control\\Keyboard Layouts\\{0:X8}",KbList[i]);
    RegistryKey rk=Registry.LocalMachine.OpenSubKey(RegKey);
    if (rk==null)
    continue;
    string ImeName=(string)rk.GetValue("layout text");
    if (ImeName==null)
    continue;
    FImes.Add(ImeName, KbList[i]);
    }
    return FImes;
    }
    }
    }

    其他IDE及类库问题

    Visual Studio 2002 IDE的问题
    监视窗口的变量在没有走到调试点时不允许删除。
    不支持事件重新命名。
    新建的窗口有时莫名其妙地就没有了标题。
    经常启动程序后,看不到界面,必须停止调试重新运行才行。
    .Net Framework 1.0的问题
    窗体没有ActiveControl属性,这点比较不爽。
    TabControl中的选项卡的标题页无法隐藏,因此无法用来实现专家向导的界面。
    TreeView的Sorted属性为True时,insert一个节点会被自动排序。没有提供定制排序的功能。
    事件传过来的坐标是系统坐标,而不是组件坐标,需要调用PointToClient转换一下才行。
    OleDbDataReader是独占连接的方式,一个DataReader不关闭的话,其他数据集都无法使用。而Delphi的DBExpress虽然也是单向游标,但是一个组件使用时不影响其他数据组件的使用。
    对于Access的支持非常不好,使用like进行模糊查询有时会导致缓冲区溢出。

    使用C#读写文件

    下面的代码示意了如何将一个数据集中的数据写入文件。
    try
    {
    DbConn.Open();
    OleDbDataReader Reader = CommandLog.ExecuteReader();
    try
    {
    StreamWriter sw = new StreamWriter(saveFile, false,
    Encoding.ASCII);

    while (Reader.Read() )
    {

    String s=Reader["IP"]+" - - ";
    DateTime dt=(DateTime)Reader["VisitDate"];
    //使用英美的日期格式格式化日期字符串
    CultureInfo ci = new CultureInfo("en-US");

    String VisitDate=dt.ToString("dd/MMM/yyyy:hh:mm:ss", ci);

    s= s+ "["+VisitDate+" -0700] ";
    String Ref=(String)Reader["Referer"];
    //如果没有Refer,则
    if (Ref.Trim()!="")
    {

    s=s+"\"GET / HTTP/1.1\" 200 23989 \""+Ref+"\" \""+Reader["UserAgent"]+"\"";
    sw.WriteLine(s);
    }

    }

    sw.Close();
    }
    finally
    {
    Reader.Close();
    }

    }
    catch (Exception e)
    {
    Console.WriteLine("发生异常\n{0}", e.Message);
    }
    }

    格式化字符串

    string result=String.Format("Select * from TblCategory where (ParentId={0}) order by CategoryIndex", Pid);

    C#Builder Open Tools Api的Bug

    1. CodeDom无法获得构造函数的行号。
    2. 无法获得Internal Protected 成员。
    3. 无法将文件从项目中删除
    4. C#Builder IOTAModuleInfo.ModuleType返回的全是空串
    5. 不支持IOTASearchOptions接口。

    使用反射动态设定组件属性

    通过typeof(Component)获得Type对象,然后调用GetProperties获得属性列表,对于特定属性可以通过
    GetProperty方法获得PropertyInfo对象,然后调用PropertyInfo对象的SetValue来设定值。示例如下:

    System.Type btnType=button1.GetType();
    PropertyInfo[] props=btnType.GetProperties();

    foreach (PropertyInfo prop in props){
    if (prop.Name=="Text")
    prop.SetValue(button1, "test", null);
    }

    要想通知IDE组件属性的变更,加下面代码示例:

    PropertyDescriptor backColorProp =
    TypeDescriptor.GetProperties(Control)["BackColor"];

    if (backColorProp != null) {
    backColorProp.SetValue(Control, Color.Green);

    也可以通过IComponentChangeService来实现通知。

    对StringCollection进行排序

    StringCollection类对应于Delphi中的TStringList类,但是同TStringList类相比,缺少了排序的功能,为此我写了一个方法,可以对StringCollection进行排序。

    //对StringCollection进行排序
    public static void Sort(StringCollection Strs,bool CaseSensitive) {
    IComparer comparer=null;
    if (CaseSensitive)
    comparer=Comparer.DefaultInvariant;
    else
    comparer=CaseInsensitiveComparer.DefaultInvariant;
    QuickSort(Strs, 0, Strs.Count - 1, comparer);
    }

    private static void QuickSort(StringCollection Strs, int L,int R, IComparer comparer) {
    while (true) {
    int I = L;
    int J = R;
    int P = (L + R) / 2;
    while (true) {
    while (comparer.Compare(Strs[I], Strs[P]) < 0)
    I++;
    while (comparer.Compare(Strs[J], Strs[P]) > 0)
    J--;
    if (I <= J) {
    ExchangeStrings(Strs, I, J);
    if (P == I)
    P = J;
    else if (P == J)
    P = I;
    I++;
    J--;
    }
    if (I > J)
    break;
    }
    if (L < J)
    QuickSort(Strs, L, J, comparer);
    L = I;
    if (I >= R)
    break;
    }
    }

    //交换字符串的位置
    public static void ExchangeStrings(StringCollection Strs, int I, int J ) {
    string si=Strs[I];
    string sj=Strs[J];
    Strs.RemoveAt(I);
    Strs.Insert(I,sj);
    Strs.RemoveAt(J);
    Strs.Insert(J,si);
    }

    从Assemble中加载自定义资源

    用Resourcer向Resx文件中添加图标、位图或者字符串等资源后,调用下面示例代码就可以加载资源了:

    //加载资源
    ResourceManager rm =
    new ResourceManager("SharpPlus.Resources", Assembly.GetExecutingAssembly());
    Icon LogoIcon=(Icon)rm.GetObject("Logo.ico");

    将字符串复制到剪贴板

    Clipboard.SetDataObject("Test");

    获取程序文件的版本信息

    //获得运行时的Assembly的版本信息
    public static string GetAssemblyVersion()
    {
    Assembly myAssembly =Assembly.GetExecutingAssembly();
    FileVersionInfo myFileVersion =FileVersionInfo.GetVersionInfo(myAssembly.Location);
    return string.Format("{0}.{1}.{2}",myFileVersion.FileMajorPart, myFileVersion.FileMinorPart, myFileVersion.FileBuildPart);
    }

    利用反射动态加载Assembly动态执行类型方法

    string dllName="OverSeer.dll";
    Type t=ReflectionUtils.GetType(dllName, "uDbg.Unit");
    dt=ReflectionUtils.GetType(dllName, "uDbg.TNxDebugger");
    //MethodInfo mi=t.GetMethod("Debugger", BindingFlags.Static | BindingFlags.Public);
    //Object debugger=mi.Invoke(null,null);
    if (t==null)
    return;
    //动态执行静态Debugger方法
    debugger=t.InvokeMember("Debugger", BindingFlags.Public| BindingFlags.Static | BindingFlags.InvokeMethod, null,null, null);

    //根据Assembly名称和类型名称动态获取类型元数据
    public static Type GetType(string AssemblyName, string TypeName)
    {
    FileInfo info=new FileInfo(AssemblyName);
    if (!info.Exists)
    return null;
    Assembly a=Assembly.LoadFrom(AssemblyName);
    //Todo:处理异常
    return a.GetType(TypeName);
    }

  • 相关阅读:
    [MSDN] How to Debug a Release Build
    抽象成员 虚方法
    强制类型转换符 和 as 运算符
    一份超长的MySQL学习笔记
    Java反射基础
    c3p0config.xml
    一个JDBC封装工具类
    Spring5——IOC操作Bean管理(基于xml文件)
    Android游戏开发大全
    移除项目里的所有.svn命令
  • 原文地址:https://www.cnblogs.com/temptation/p/407332.html
Copyright © 2020-2023  润新知