C# 笔记

2020-04-29  本文已影响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这个特权账户,或者具有特殊权限的用户账户,而非其它受限的账户。

上一篇下一篇

猜你喜欢

热点阅读