C# 笔记

作者: 宅玖蔡 | 来源:发表于2020-04-29 11:15 被阅读0次

    0、获取输入参数的实际名称

    使用nameof

    public static IEnumerable<T> Shrink<T>(this IEnumerable<T> list, double prop, bool both_end)
    {
        if (list == null)
            throw new ArgumentNullException(paramName: nameof(list));
        if (prop < 0)
            throw new ArgumentOutOfRangeException(paramName: nameof(prop));
    }
    

    1、枚举的转换

    public enum GnssMessageType { Unknown = -1, GPGGA = 218, GPRMC = 225 }
    
    string typeName = "GPGGA";
    GnssMessageType type;
    //尝试转换为枚举,假如转换失败,标记为未知类型
    Enum.TryParse<GnssMessageType>(typeName, out type);
    if (!Enum.IsDefined(typeof(GnssMessageType), type))
        type = GnssMessageType.Unknown;
    

    2、C#判断对象类型

    Student student_jack = new Student("Jack");
    bool is_jack_student = student_jack is Student;
    

    3、用反射动态绑定控件事件

    容器窗体内包括一个1列2行的TableLayoutPanel控件,第1行为若干按钮,第2行等待填充其它控件;重载构造器并添加绑定事件方法


    容器窗体
            /// <summary>
            /// 容器窗体初始化,指定要填充的子控件以及停靠方式
            /// </summary>
            /// <param name="child">待填充进来的子控件</param>
            /// <param name="dockStyle">停靠方式,为空则按默认方式停靠</param>
            public FormContainer(Control child, DockStyle? dockStyle)
            {
                InitializeComponent();
                if (child == null)
                    return;
                //假如为Windows窗体,则取消顶级窗口属性
                if (child is Form)
                    ((Form)child).TopLevel = false;
                this.tableLayoutPanel_Main.Controls.Add(child, 0, 1); //在TableLayoutControl第1列第2行添加
                this.Name = child.Name; //改变容器类名称
                this.AddClickEvents(this, child); //绑定点击方法
                if (dockStyle != null)
                    child.Dock = dockStyle.Value;
            }
    
            /// <summary>
            /// 将指定控件的子控件Click委托绑定其它控件中的Click方法,假如有子控件则在子控件中查找
            /// </summary>
            /// <param name="body">在其下子控件中寻找Click委托的控件</param>
            /// <param name="child">在其中寻找绑定方法的控件</param>
            private void AddClickEvents(Control body, Control child)
            {
                //循环子控件,,否则
                foreach (Control control in body.Controls)
                {
                    //假如子控件亦有子控件则递归
                    if (control.HasChildren)
                    {
                        this.AddClickEvents(control, child);
                        continue;
                    }
                    //假如为按钮且可见则绑定Click方法,方法名称为[按钮名称_Click],区分大小写
                    else if (control is Button/* && control.Visible*/)
                    {
                        EventInfo click = control.GetType().GetEvent("Click"); //按名称寻找事件
                        MethodInfo method = child.GetType().GetMethod(string.Format("{0}_Click", control.Name), new Type[] { typeof(object), typeof(EventArgs) }); //按名称与参数寻找方法
                        if (method == null)
                            continue;
                        Delegate handler = Delegate.CreateDelegate(click.EventHandlerType, child, method); //TODO 第二个参数需为方法所在的控件
                        click.AddEventHandler(control, handler);
                    }
                }
            }
    

    新建用户控件并添加名称符合规则的Click方法


    用户控件
            /// <summary>
            /// 新增方法
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            public void Button_Add_Click(object sender, EventArgs e)
            {
                object obj = this;
                this.label1.Text = "444";
                this.label3.Text = "666";
            }
    

    调用方法

                FormContainer container = new FormContainer(new UserControlExample(), DockStyle.Fill);
                this.ShowForm(container);
    

    结果


    调用结果

    4、使用LAMBDA表达式(LINQ)时同时给出索引

    string[] names = new string[] { "Jack", "Jameson", "Laura" };
    List<string> list = 
    names.Select((name, index) => new { name, index })
         .Where(a => a.name.StartsWith("L") || a.index == 0).ToList();
    

    5、C#粘贴板

    1)复制到粘贴板

    Clipboard.SetDataObject(this.textBox_Info.Text);
    Clipboard.SetText(this.textBox_Info.Text);
    this.label_Copied.Visible = true;
    new Thread(new ThreadStart(() =>
    {
        Thread.Sleep(1500);
        this.label_Copied.SafeInvoke(() =>
        {
            this.label_Copied.Visible = false;
        });
    }))
    { IsBackground = true }.Start();
    

    2)从粘贴板粘贴

    // Declares an IDataObject to hold the data returned from the clipboard.
    // Retrieves the data from the clipboard.
    IDataObject iData = Clipboard.GetDataObject();
    
    // Determines whether the data is in a format you can use.
    if(iData.GetDataPresent(DataFormats.Text))
    {
        // Yes it is, so display it in a text box.
        textBox2.Text = (String)iData.GetData(DataFormats.Text); 
    }
    

    6、protobuf-net的应用问题

    0) NUGET安装

    VS内进入程序包管理器控制台输入

    PM> Install-Package protobuf-net

    卸载则输入以下命令

    PM> Uninstall-Package protobuf-net

    1) ProtoMember序号问题

    不大于536870911,且应避开范围19000-19999
    Notes for Identifiers

    they must be positive integers (for best portability, they should be <= 536870911 and not in the range 19000-19999)

    2) 构造器

    假如在反序列化过程中存在有参构造器,则无参构造器亦必须同时存在

        [ProtoContract]
        public class SomeObject
        {
            [ProtoMember(1)]
            public int Index { get; set; }
    
            [ProtoMember(2)]
            public DateTime Time { get; set; }
    
            /// <summary>
            /// 无参构造器,在反序列化过程中假如存在有参构造器,则亦必须同时存在
            /// </summary>
            public SomeObject() { }
    
            public SomeObject(int index, DateTime time)
            {
                this.Index = index;
                this.Time = time;
            }
        }
    

    3) 字段支持

    假如引用类型字段要序列化,则该类型在定义时必须支持序列化,否则不需要

        [ProtoContract]
        public class OtherObject
        {
            [ProtoMember(1)]
            public string Message { get; set; }
        }
    
        [ProtoContract]
        public class SomeType
        {
            [ProtoMember(1)]
            public int Id { get; set; }
    
            [ProtoMember(2)]
            public string Name { get; set; }
    
            [ProtoMember(3)]
            public List<int> Values { get; set; }
    
            /// <summary>
            /// Object属性,假如引用类型字段要序列化,则该类型在定义时必须支持序列化,否则不需要
            /// </summary>
            [ProtoMember(4)]
            public SomeObject Object { get; set; }
    
            /// <summary>
            /// OtherObject属性,假如引用类型字段要序列化,则该类型在定义时必须支持序列化,否则不需要
            /// </summary>
            [ProtoMember(5)]
            public OtherObject OtherObject { get; set; }
    
            public override string ToString()
            {
                return string.Format("{0}: {1}", this.Id, this.Name);
            }
        }
    

    4) 继承

    继承关系必须显性地声明,且在同一个类的范围内ProtoInclude与ProtoMember的序号不可重复

        [ProtoContract]
        [ProtoInclude(1, typeof(SomeDerivedType))]
        public class SomeBaseType
        {
            [ProtoMember(2)]
            public int Id { get; set; }
        }
    
        [ProtoContract]
        public class SomeDerivedType : SomeBaseType
        {
            [ProtoMember(1)]
            public string Name { get; set; }
        }
    

    7、泛型中类型参数的初始化

    1) default(T)

        public class RadioWrapper<T>
        {
            private readonly T _base;
    
            public RadioWrapper()
            {
                this._base = default(T);
            }
        }
    

    2) 对T添加约束

        public class RadioWrapper<T> where T : new()
        {
            private readonly T _base = default(T);
    
            public RadioWrapper()
            {
                this._base = new T();
            }
        }
    

    假如要对多个参数类型进行约束:

    public DataSet MultiQuery<Conn, Adapter>(string connStr, IEnumerable<string> sqlStrings)
      where Conn : DbConnection, IDisposable
      where Adapter : DbConnection, IDisposable
    {
        ....
    }
    

    3) 调用CreateInstance()方法

        public class RadioWrapper<T> where T
        {
            private readonly T _base = default(T);
            private readonly Type _type;
    
            public RadioWrapper()
            {
                this._type = typeof(T);
                this._base = (T)Activator.CreateInstance(this._type);
                //this._base = (T)Assembly.GetAssembly(this._type).CreateInstance(this._type.ToString());
            }
        }
    

    8、初始化泛型类

        public class Machines
        {
            public Dictionary<string, object> DictMachine { get; set; }
    
            public Machines()
            {
                this.DictMachine = new Dictionary<string, object>();
                List<string> names = new List<string>() { "S1", "R1" };
                Type classType = Type.GetType("ConnectServerWrapper.RadioWrapper`1"); //获取泛型类类型,命名空间+类名
                foreach (var name in names)
                {
                    Type T = Type.GetType(string.Format("gprotocol.Radio_{0}, connect_server", name)); //获取类型参数的类型,引用的类库需指明程序集名称
                    Type genericType = classType.MakeGenericType(T); //替代由当前泛型类型定义的类型参数组成的类型数组的元素,并返回表示结果构造类型的 System.Type 对象
                    this.DictMachine.Add(name, Activator.CreateInstance(genericType));
                }
            }
        }
    

    9、泛型类获取、设置属性值,调用方法

    1) 获取属性值

    Type type = typeof(T);
    T t = (T)Activator.CreateInstance(type);
    PropertyInfo property = type.GetProperty("FloatList");
    List<float> float_list = (List<float>)property.GetValue(t);
    float_list.Clear();
    float_list.AddRange(new float[] { 0, 0, 0 });
    

    2) 设置属性值

    Type type = typeof(T);
    T t = (T)Activator.CreateInstance(type);
    PropertyInfo property = type.GetProperty("FloatList");
    property.SetValue(t, new List<float>() { 0, 0, 0 });
    

    3) 获取方法并调用

    public static bool IsConnOpen<T>(string connStr) where T : DbConnection, IDisposable
    {
        Type type = typeof(T);
        T connection = (T)Activator.CreateInstance(type, connStr);
        MethodInfo openMethod = type.GetMethod("Open");
        PropertyInfo stateProp = type.GetProperty("State");
        ConnectionState state = ConnectionState.Closed;
    
        openMethod.Invoke(connection, null);
        state = (ConnectionState)stateProp.GetValue(connection);
    }
    

    10、反射调用静态泛型方法

    //通过反射调用Enumerable.ElementAt方法获取List<int>的元素
    List<int> list = new List<int>() { 0, 0, 0 };
    //获取List<int>或其它类型集合所有的泛型的类型参数
    Type[] genericTypes = list.GetType().GenericTypeArguments;
    object result;
    //仅在有一个类型参数的情况下继续
    if (genericTypes != null && genericTypes.Length == 1)
    {
        //获取方法所在的类型
        Type enumerableType = typeof(Enumerable);
        //获取方法,并通过获取到的类型参数转化为泛型方法
        MethodInfo method = enumerableType.GetMethod("ElementAt");
        MethodInfo methodGeneric = method.MakeGenericMethod(genericTypes[0]);
        //执行泛型方法,假如为静态方法,则第一个参数为null,第二个参数为方法所需参数的数组
        result = methodGeneric.Invoke(null, new object[] { list, 0 });
    }
    

    11、CommonLib.DataUtil.OracleProvider调用存储过程返回结果集

    包头:

    create or replace package pag_example is
     type mycursor is ref cursor;
    
     Procedure p_example (var_flowid Varchar2,
                var_fremark Varchar2,
                myresult out mycursor);
    
    end pag_example;
    

    包体:

    create or replace package body pag_example is
    
     Procedure p_example (var_flowid Varchar2,
                var_fremark Varchar2,
                myresult out mycursor) is
     begin
    
      open myresult for
    
       select * from table_example
    
     end p_example;
    end pag_example;
    

    调用过程:

    OracleProvider provider = new ("localhost", "orcl", "user_name", "user_pass");
    IDataParameter[] parameters = new IDataParameter[3]
    {
        new OracleParameter("var_flowid", OracleDbType.Varchar2, ParameterDirection.Input) { Value = "1" },
        new OracleParameter("var_fremark", OracleDbType.Varchar2) { Value = "Q" },
        new OracleParameter("myresult", OracleDbType.RefCursor, ParameterDirection.Output)
    };
    DataTable dataTable = this.provider.RunProcedureQuery("pag_get_camera.p_camera", parameters).Tables[0];
    

    12、Json.NET控制字段名称拼写方式与格式

    //序列化设置
    JsonSerializerSettings setting = new JsonSerializerSettings()
    {
        ContractResolver = new DefaultContractResolver()
        {
            NamingStrategy = new CamelCaseNamingStrategy() //lowerCamelCase
            //NamingStrategy = new DefaultNamingStrategy() //UpperCamelCase
            //NamingStrategy = new SnakeCaseNamingStrategy() //snake_case
            //NamingStrategy = new KebabCaseNamingStrategy() //kebab-case
        },
        Formatting = Formatting.None //无特殊格式,一行输出
        //Formatting = Formatting.Indented //带缩进多行输出
    };
    string result = JsonConvert.SerializeObject(new JsonSource(), setting);
    
    name demo
    Default PersonName
    CamelCase personName
    SnakeCase person_name
    KebabCase person-name

    13、WPF在代码中动态添加Style

    deviceStatus.Style = FindResource("FontAwesome") as Style;
    deviceStatus.Text = FindResource("fa-lightbulb-o").ToString();
    

    14、WPF在其它Style的基础上创建Style

    基础Style

        <Style x:Key="TabItemExWithUnderLineStyle"  TargetType="{x:Type TabItem}">
        ...
        </Style>
    

    衍生Style

        <Style x:Key="ProjectMenuTabItemStyle" TargetType="TabItem" BasedOn="{StaticResource TabItemExWithUnderLineStyle}">
            <Setter Property="Cursor" Value="Hand"/>
            <Setter Property="Width" Value="115"/>
            <Setter Property="Height" Value="38"/>
            <Setter Property="Margin" Value="0, 0, 0, 0"/>
            <Setter Property="Foreground" Value="Black"/>
        </Style>
    

    注意,基础Style一定要在衍生Style之前声明

    15、string.Format格式字符串包含"{"与"}"的处理方式

    因为String.Format会识别{},来用参数代替,但是如果字符串中包含{或者},则需要用{{ 来代替字符 {,用}} 代替 }
    例如

    json.Append(String.Format("{\"total\":{0},\"row\":{1}}", lineCount, strJSON));
    

    运行时会报错,
    若用如下代码来代替,则可以正确运行:

    json.Append(String.Format("{{\"total\":{0},\"row\":{1}}}", lineCount, strJSON));
    

    16、类或结构实现比较器

    实现IComparable<T>接口

    public struct DistAlarm : IComparable<DistAlarm>
    {
        public double Dist { get; set; }
    
        public int Alarm { get; set; }
    
        public DistAlarm(double dist, int alarm)
        {
            Dist = dist;
            Alarm = alarm;
        }
    
        public override int GetHashCode()
        {
            return Dist.GetHashCode() | Alarm.GetHashCode();
        }
    
        #region 是否相等的比较
        public override bool Equals(object obj)
        {
            return obj is DistAlarm && Dist == ((DistAlarm)obj).Dist && Alarm == ((DistAlarm)obj).Alarm;
        }
    
        public static bool operator ==(DistAlarm left, DistAlarm right)
        {
            return left.Equals(right);
        }
    
        public static bool operator !=(DistAlarm left, DistAlarm right)
        {
            return !(left == right);
        }
        #endregion
    
        #region 大小的比较
        public int CompareTo(DistAlarm other)
        {
            return Dist.CompareTo(other.Dist);
        }
    
        public static bool operator <(DistAlarm left, DistAlarm right)
        {
            return left.CompareTo(right) < 0;
        }
    
        public static bool operator <=(DistAlarm left, DistAlarm right)
        {
            return left.CompareTo(right) <= 0;
        }
    
        public static bool operator >(DistAlarm left, DistAlarm right)
        {
            return left.CompareTo(right) > 0;
        }
    
        public static bool operator >=(DistAlarm left, DistAlarm right)
        {
            return left.CompareTo(right) >= 0;
        }
        #endregion
    }
    

    17、含中文字符串与PLC字符类型的互相转化

    含中文字符串编码为sbyte类型(指取值范围)数组,然后可根据需要将这些值写入PLC字符类型标签的每一位中

    string source = "首钢京唐A"; //源中文字符串
    byte[] bytes = Encoding.Default.GetBytes(source); //用默认编码编码为byte数组
    int[] numbers = bytes.Select(b => b > 127 ? b - 256 : b).ToArray(); //调整取值范围(AB公司PLC内字符串每一位字符取值范围为-128~127)
    //numbers = { -54, -41, -72, -42, -66, -87, -52, -58, 65 }
    

    从PLC字符类型标签中分别获取每一位的值,并解码为含中文字符串

    方法1:转化为URL编码并通过GB2312解码为字符串

    string step1 = string.Join(string.Empty, numbers.Select(n => n < 0 ? n + 256 : n).Select(n => '%' + n.ToString("X2")).ToArray());
    //step1 = "%CA%D7%B8%D6%BE%A9%CC%C6%41";
    string test = HttpUtility.UrlDecode(step1, Encoding.GetEncoding("GB2312"));
    

    方法2:调整回byte类型取值范围并解码为字符串

    byte[] step2 = numbers.Select(n => (byte)(n < 0 ? n + 256 : n)).ToArray();
    test = Encoding.Default.GetString(step2);
    

    18、使程序以获取管理员权限的方式运行

    一、启用ClickOnce安全设置

    在Visual Studio中,解决方案资源管理器--右键项目名称--属性,找到“安全性”选项,勾选“启用ClickOnce安全设置”


    启用ClickOnce安全设置

    二、修改清单文件

    上一步操作后,项目下面会多出一个名为“app.manifest”的文件

    app.manifest文件位置

    选中它,并找到元素<requestedExecutionLevel level="asInvoker" uiAccess="false" />,将其改为:<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />,

          <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
            <!-- UAC 清单选项
                 如果想要更改 Windows 用户帐户控制级别,请使用
                 以下节点之一替换 requestedExecutionLevel 节点。n
            <requestedExecutionLevel  level="asInvoker" uiAccess="false" />
            <requestedExecutionLevel  level="requireAdministrator" uiAccess="false" />
            <requestedExecutionLevel  level="highestAvailable" uiAccess="false" />
    
                指定 requestedExecutionLevel 元素将禁用文件和注册表虚拟化。
                如果你的应用程序需要此虚拟化来实现向后兼容性,则删除此
                元素。
            -->
            <!--<requestedExecutionLevel  level="asInvoker" uiAccess="false" />-->
            <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
          </requestedPrivileges>
    

    三、取消勾选ClickOnce

    改正后,不要急于重新编译生成,再次打开“属性--安全性”界面,将“启用ClickOnce安全设置”前面的勾去掉后再编译运行,不然程序会报错无法运行。

    取消勾选ClickOnce

    四、重新编译运行程序

    打开程序时,会提示“用户账户控制”来获取管理员权限运行,点击“是”获取管理员权限。

    19、使Windows服务具有足够的权限

    设置为LocalSystem账户

    将服务的启动账户设置为LocalSystem这个特权账户,或者具有特殊权限的用户账户,而非其它受限的账户。

    相关文章

      网友评论

          本文标题:C# 笔记

          本文链接:https://www.haomeiwen.com/subject/ymlgwhtx.html