C# 笔记

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());
        }
    }

4) 泛型类型的比较与转换

比较

bool isInt = typeof(T) == typeof(int);

转换

int source = 36;
T data = (T)Convert.ChangeType(source, typeof(T));

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"); //获取泛型类类型,命名空间+类名
            Type classType = typeof(RadioWrapper<>); //获取泛型类类型
            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");
    //假如有不止一个同名方法,则需指定参数的数量和类型,假如没有参数则用Type.EmptyTypes指定
    MethodInfo openMethod = type.GetMethod("Open", Type.EmptyTypes);
    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 });
}

10.1、反射获取命名空间下所有类

定义

using System.Reflection;
private Type[] GetTypesInNamespace(Assembly assembly, string nameSpace)
{
    return assembly.GetTypes().Where(t => String.Equals(t.Namespace, nameSpace, StringComparison.Ordinal)).ToArray();
}

调用

Type[] typelist = GetTypesInNamespace(Assembly.GetExecutingAssembly(), "MyNamespace");

for (int i = 0; i < typelist.Length; i++)
{
    Console.WriteLine(typelist[i].Name);
}

11、获取程序集的各种方法

11.1、获取当前正在执行的程序集

假如当前执行代码在独立的链接库,则将获取到链接库的程序集

//当前执行的代码的程序集
var assemply = Assembly.GetExecutingAssembly();

11.2、获取进程可执行文件的程序集

与执行代码或调用的方法所在的程序集无关,将获取到的是入口程序(可执行文件)所在的程序集

//获取当前可执行文件的程序集
var assemply = Assembly.GetEntryAssembly();

11.3、根据程序集长名称加载程序集

//根据长名称获取程序集
var assemply = Assembly.Load("IntercommConsole.OpcOnly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");

12、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];

13、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

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

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

15、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之前声明

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

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

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

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

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

17、类或结构实现比较器

实现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
}

18、含中文字符串与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);

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

一、启用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

四、重新编译运行程序

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

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

设置为LocalSystem账户

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

21、C#操作进程的显示与隐藏

using System.Runtime.InteropServices;

[DllImport("user32.dll", EntryPoint = "ShowWindow", SetLastError = true)]
static extern bool ShowWindow(IntPtr hWnd, uint nCmdShow);

Intptr intptr = Process.GetProcessesByName("name").MainWindowHandle;
//隐藏主窗体
ShowWindow(intptr, 0);
//显示主窗体
ShowWindow(intptr, 1);

22、修改App.config文件中的AppSetting配置

//打开当前进程的配置文件
var conf = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
//配置文件添加键值对节点
conf.AppSettings.Settings.Add("ConfigFileName", "Config.ini");
//保存到当前配置文件(在生成路径下)
//conf.Save(ConfigurationSaveMode.Full);
conf.Save();

23、在高并发的场景下,Dictionary.Add()方法可能会报出超出索引的错误,因此应使用ConcurrentDictionary

ConcurrentDictionary<int, Form> dict = new ConcurrentDictionary<int, Form>();
if (!dict.ContainsKey(0))
    while (dict.TryAdd(0, new Form()))
        break;

24、在C#中使用元组(Tuple)

  • 在程序包管理器中安装包System.ValueTuple,或在程序包管理器控制台中输入命令

PM> Install-Package System.ValueTuple

并执行

  • 测试元组的交换赋值功能
int i1 = 3, i2 = 6;
(i1, i2) = (i2, i1);
Console.WriteLine("i1 = " + i1 + ", i2 = " + i2);
// i1 = 6, i2 = 3;

25、在C#中使用Winform继承功能时所需要注意的

1) 父窗体的构造器与类型定义

在打开子窗体的设计视图时,vs会默认初始化一个父窗体的实例,因此父窗体必须有一个不包含任何参数的默认构造器,同时不可为抽象类,因为抽象类无法实例化

2) 假如打开子窗体的设计视图时卡死

如上文所述,子窗体的设计视图被打开时,会有一个父窗体的默认实例被初始化,在此过程中会执行默认构造器中的代码,假如父窗体包含Timer控件且Timer设为可用、且同时还会启动Timer并执行Timer的Elapse方法的话,那需要考虑的一个可能性就是父窗体默认构造器中或Timer的执行方法中是否有报错或造成卡顿的操作

26、关于struct(结构)的构造函数

struct(结构)类型在C#中属于值类型(value type),其构造函数有一些限制:

1) struct有参构造函数中必须为所有成员(包括自动实现的属性,后面对于这点的说明从略)赋值

举个例子:

public struct Value
{
    public int Value1 { get; }
    private int Value2;

    public Value(int value1, int value2)
    {
        Value1 = value1;
        Value2 = value2;
    }
}

上述代码中,我们在 Value 结构的有参构造函数中对其所有的成员进行了赋值,否则就会产生编译错误:

public struct Value
{
    public int Value1 { get; }
    private int Value2;

    public Value(int value1, int value2)
    {
        Value1 = value1;
        // cause error here ...
        //Value2 = value2;
    }
}

有时候,我们可能仅希望在 struct 的有参构造函数中初始化某些成员,其他的成员保持其默认值即可,怎么办呢?
方法其实挺简单,在有参构造函数中首先调用无参构造函数即可:

public struct Value
{
    public int Value1 { get; }
    private int Value2;

    // call parameterless constructor first
    public Value(int value1) : this()
    {
        Value1 = value1;
    }
}

2) struct 中不能定义无参构造函数

根据此规则,你不能定义一个 struct 的无参构造函数来定制化成员的初始值:

public struct Value
{
    public int Value1 { get; }
    private int Value2;

    // cause error here ...
    public Value()
    {
        Value1 = 1;
        Value2 = 1;
    }
}

可见 struct 成员的默认值不能进行定制,所以对象初始化对于 struct 的成员也不适用。

27、将ulong(UInt64)整型数值转换为2进制字符串

ulong ul = ulong.MaxValue;
//将ulong转为long
//long l = BitConverter.ToInt64(BitConverter.GetBytes(ul), 0);
long l = unchecked((long)ul);
//将long转为2进制
string binary = Convert.ToString(l, 2);

28、将8位无符号或有符号整型数字转为汉字

//调整为8位无符号整型的值区间,存储校正后的值,加上索引以在出问题时定位来源
var array = numbers.Select(n => n < 0 ? n + 256 : n).Select((value, index) => new { value, index });
//不在区间范围内的值
var invalid = array.FirstOrDefault(p => !p.value.Between(byte.MinValue, byte.MaxValue));
if (invalid != null)
{
    int original = numbers.ElementAt(invalid.index); //在源数列中对应的原始值
    throw new ArgumentOutOfRangeException(nameof(numbers), invalid.value, $"源数列中索引{invalid.index}处的值{original}加上校正值后变为{invalid.value},超出了8位无符号整型的值区间[0,255]");
}
//转为URL形式的中文编码(排除0,中文字符编码中2个字节均不为0)
string encoded = string.Join(string.Empty, array.Where(p => p.value != 0).Select(p => '%' + p.value.ToString("X2")).ToArray());
//用解码URL的方式解码中文字符
string result = System.Web.HttpUtility.UrlDecode(encoded, Encoding.GetEncoding("GB2312"));

29、Func<>委托

有输入参数、有返回值
有输入参数、无返回值见下一节delegate委托

1) 如何声明和使用 Func<T1,T2,TResult> 委托

此示例声明一个变量,并为其分配一个 Func<T1,T2,TResult> lambda 表达式,该表达式采用值 StringInt32 值作为参数

using System;
using System.Collections.Generic;
using System.Linq;

public class Func3Example
{
   public static void Main()
   {
      Func<String, int, bool> predicate = (str, index) => str.Length == index;

      String[] words = { "orange", "apple", "Article", "elephant", "star", "and" };
      IEnumerable<String> aWords = words.Where(predicate).Select(str => str);

      foreach (String word in aWords)
         Console.WriteLine(word);
   }
}

2) 可以将委托与 C# 中的匿名方法配合使用,如以下示例所示。 (有关匿名方法简介,请参阅 Anonymous Methods.)

using System;

public class Anonymous
{
   public static void Main()
   {
      Func<string, int, string[]> extractMeth = delegate(string s, int i)
         { char[] delimiters = new char[] {' '};
           return i > 0 ? s.Split(delimiters, i) : s.Split(delimiters);
         };

      string title = "The Scarlet Letter";
      // Use Func instance to call ExtractWords method and display result
      foreach (string word in extractMeth(title, 5))
         Console.WriteLine(word);
   }
}

3) 还可以将 lambda 表达式分配给委托,如以下示例所示。 (有关 lambda 表达式简介,请参阅 Lambda 表达式 (VB) Lambda 表达式 (C#) lambda 表达式 (F#) .)

using System;

public class LambdaExpression
{
   public static void Main()
   {
      char[] separators = new char[] {' '};
      Func<string, int, string[]> extract = (s, i) =>
           i > 0 ? s.Split(separators, i) : s.Split(separators) ;

      string title = "The Scarlet Letter";
      // Use Func instance to call ExtractWords method and display result
      foreach (string word in extract(title, 5))
         Console.WriteLine(word);
   }
}

30、delegate委托

Delegate至少0个参数,至多32个参数,可以无返回值,也可以指定返回值类型。
例如

//表示有两个参数,并返回int型
public delegate int MethodtDelegate(int x, int y);
//用委托为事件添加内容
StateChanged += new TaskStateChangedEventHandler(delegate (object obj, TaskStateChangedEventArgs args)
{
    if (args.State == ServiceState.Started)
        return;
    RestartUrself();
    ThreadLoop.Abort();
});

31、正态分布曲线的公式与实现

正态分布曲线与公式
/// <summary>
/// 计算正态分布函数的公式,计算时使用给定的平均值μ(mu)与标准差σ(sigma),输入x坐标,输出正太分布曲线在这个位置的y坐标值
/// https://i.postimg.cc/P5NxZ0WX/image.png
/// </summary>
/// <param name="x">x坐标值</param>
/// <param name="mean">平均值μ(mu)</param>
/// <param name="stdDev">标准差σ(sigma)</param>
/// <returns></returns>
public static double NormalDistribution(double x, double mean, double stdDev)
{
    double a = 1 / (stdDev * Math.Sqrt(2 * Math.PI));
    double b = Math.Pow(Math.E, -Math.Pow(x - mean, 2) / (2 * Math.Pow(stdDev, 2)));
    return a * b;
}

32、XML文档注释

  1. 换行时添加<para/>标识
/// <summary>
/// 利用特定的IP、端口与TCP服务端连接<para/>
/// 假如连接则发送报文时不包含服务端IP地址、端口号,否则发送时需指定
/// </summary>

33、StreamWriter使用ANSI编码写入文件

StreamWriter sw = new StreamWriter("D:\\test4.csv", true, Encoding.GetEncoding("GB2312"));
sw.WriteLine("col1,col2,col3");
sw.WriteLine("繁體中文,2,3");
sw.WriteLine("简体中文,2,3");
sw.Close(); //保证内容正常保存

34、System.Threading.Tasks.Task的使用

1) 定义异步操作的方法,输入参数自定,返回Task对象

/// <summary>
/// 进行异步操作
/// </summary>
/// <returns></returns>
private Task<string> DoSomethingAsync()
{
    TaskCompletionSource<string> taskCompletionSource = new TaskCompletionSource<string>();
    try
    {
        //进行一些操作
        string result = "成功";
        taskCompletionSource.TrySetResult(result);
    }
    catch (Exception e)
    {
        taskCompletionSource.TrySetException(e);
    }
    return taskCompletionSource.Task;
}

2) 执行Task对象,判断是否超时

/// <summary>
/// 执行任务
/// </summary>
/// <param name="task"></param>
/// <returns></returns>
/// <exception cref="TimeoutException"></exception>
public string RunTask(Task<string> task)
{
    bool intime; //是否在响应时间内完成
    try { intime = task.Wait(5000); }
    catch (Exception e)
    {
        while (e.InnerException != null)
            e = e.InnerException;
        throw e;
    }
    if (!intime)
        throw new TimeoutException("Timeout");
    else
    {
        return task.Result;
    }
}

3) 例子

/// <summary>
/// 执行例子
/// </summary>
public static void RunExample()
{
    var example = new TaskExample();
    var task = example.DoSomethingAsync();
    string result = example.RunTask(task);
    Console.WriteLine(result);
}

35、严谨使用相对路径

当使用cmd启动、bat启动或系统启动时,程序环境中使用的目录(的其中一些)可能会变得不正确,造成直接使用相对路径也不正确,因此使用相对路径时最好进行一些处理

//判断是否存在卷分隔符,假如不存在则为相对路径,在前侧加上程序所在路径
string path = "..\dir\demo.exe";
string fullPath = path.Contains(Path.VolumeSeparatorChar) ? path : AppDomain.CurrentDomain.BaseDirectory + path;

36、在 Visual Studio 中使用实时调试器进行调试

实时调试器界面

使用实时调试器进行调试 - Visual Studio (Windows) | Microsoft Learn

1) 启用 Windows 窗体的实时调试

默认情况下,Windows 窗体应用具有一个顶级异常处理程序,可让应用保持运行(如果能够恢复)。 如果 Windows 窗体应用引发未处理的异常,则会显示以下对话框:


Windows窗体异常处理对话框

若要启用实时调试而不是标准 Windows 窗体错误处理,请添加以下设置:

  • 在 machine.config 或 <应用名称>.exe.config 文件的 system.windows.forms 部分中,将 jitDebugging 值设置为 true

    <configuration>
        <system.windows.forms jitDebugging="true" />
    </configuration>
    

2) 在 Visual Studio 中启用或禁用实时调试

若要启用或禁用实时调试,必须以管理员身份运行 Visual Studio。启用或禁用实时调试会设置一个注册表项,可能需要管理员权限来更改此注册表项。 若要以管理员身份打开 Visual Studio,右键单击 Visual Studio 应用程序,然后选择以管理员身份运行
可从 Visual Studio 的“工具”>“选项”(或“调试”>“选项”)对话框中配置实时调试。

启用或禁用实时调试:

  1. 在“工具”或“调试”菜单上,选择“选项”>“调试”>“实时”。

    实时调试选项

    如果未显示“实时”菜单选项,请确保使用 Visual Studio 安装程序安装了实时调试程序。

  2. 在“启用这些代码类型的实时调试”框中,选择希望通过实时调试进行调试的代码类型:“托管”、“本机”和/或“脚本”。

  3. 选择“确定”。

如果启用了实时调试器,但它并未在应用程序崩溃或出错时打开,请参阅实时调试疑难解答

3) 从 Windows 注册表禁用实时调试

即便计算机中不再安装有 Visual Studio,仍可启用实时调试。 如果不再安装 Visual Studio,则可以通过编辑 Windows 注册表来禁用实时调试。

若要通过编辑注册表禁用实时调试,请执行以下操作:

  1. 从 Windows“开始”菜单,运行“注册表编辑器”(regedit.exe) 。
  2. 在“注册表编辑器”窗口中,找到并删除下列注册表项(如果存在):
    • HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft.NETFramework\DbgManagedDebugger
    • HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\AeDebug\Debugger
      注册表项
  3. 如果存在以下注册表项,请将其删除:
    • HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft.NETFramework\DbgManagedDebugger
    • HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug\Debugger

确保不要删除或更改任何其他注册表项。

  1. 关闭“注册表编辑器”窗口。

4) 实时调试疑难解答

如果在应用崩溃时实时调试不启动,即使 Visual Studio 中已启用实时调试也是如此,则:

  • Windows 错误报告可能会接管计算机上的错误处理。

    若要修复此问题,请使用注册表编辑器向以下注册表项添加值为“已禁用”的“DWORD 值”,以及值为“1”的“数值数据” :

    • HKEY_LOCAL_MACHINE\Software\WOW6432Node\Microsoft\Windows\Windows Error Reporting

    • (对于 32 位计算机)HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\Windows Error Reporting

    有关详细信息,请参阅 .WER 设置

  • 已知的 Windows 问题可能导致实时调试器启动失败。

    修复方法是向以下注册表项添加值为“自动”的“DWORD 值”,以及值为“1”的“数值数据” :

    • HKEY_LOCAL_MACHINE\Software\WOW6432Node\Microsoft\Windows NT\CurrentVersion\AeDebug

    • (对于 32 位计算机)HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug

你可能会在实时调试过程中遇到以下错误消息:

  • 无法附加到崩溃进程。 指定的程序不是 Windows 或 MS-DOS 程序。

    调试器尝试附加到在另一个用户下运行的进程。

    若要解决此问题,请在 Visual Studio 中依次打开“调试”>“附加到进程”(或按 Ctrl + Alt + P),然后在“可用进程”列表中查找要调试的进程。 如果不清楚进程名称,请在“Visual Studio 实时调试器”对话框中查找进程 ID。 在“可用进程”列表中选择该进程,然后选择“附加” 。 选择“否”可关闭“实时调试器”对话框。

  • 未能启动调试器,因为没有用户登录。

    没有用户登录到控制台,因此没有用户会话来显示实时调试对话框。

    要解决此问题,请登录到计算机。

  • 类没有注册。

    调试器试图创建的 COM 类未进行注册,可能是由安装问题导致。

    若要修复此问题,请使用 Visual Studio 安装程序重新安装或修复 Visual Studio 安装。

37、启用或禁用Windows错误报告(WER)(组策略)

Windows 错误报告和 Windows 诊断启用指南 - Windows Client | Microsoft Learn

38、Windows错误报告(WER)的设置(注册表)

Windows程序奔溃后,禁止弹出“停止工作”对话框
WER Settings - Win32 apps | Microsoft Learn
WER 设置 - Win32 apps | Microsoft Learn

WER设置所在的注册表子项:

  • HKEY_CURRENT_USER\Software\Microsoft\Windows\Windows Error Reporting
  • HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\Windows Error Reporting

修改后即时生效

1) 涉及到的注册表值

1.1) Disabled

REG_DWORD

Possible values:
0 - Enabled (default)
1 - Disabled

Enable or disable WER

1.2) DontShowUI

REG_DWORD

Possible values:
0 - UI (default)
1 - No UI

Enable or disable the WER UI

1.3) ExcludedApplications[Application Name]

REG_SZ(字符串值)

Use WerAddExcludedApplication

List of excluded applications

2) 效果

2.1) DontShowUI值为0时

保持WER启用的效果

保持WER禁用的效果

注意:在Win11下(Win10未测试),出现上图中无论何种对话框,均会认为进程已失去响应,但在Win7则会认为程序依然有响应,导致对进程状态的判断失败

2.2) DontShowUI值为1时

进程直接退出

3) 使用脚本快速导入

新建文本文件,添加如下内容

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting]
"DontShowUI"=dword:00000001
"Disabled"=dword:00000001

保存文件并修改后缀为.reg,直接双击执行即可

39、DataGridView逐行选中并改变样式:实现

1) 属性设置

  • MultiSelect属性设置为False


    MultiSelect设为False
  • SelectionMode属性设置为FullRowSelect


    SelectionMode设为FullRowSelect

2) 代码实现

foreach (DataGridViewRow viewRow in dataGridView_Meters.Rows)
{
    //选中当前行,并选择当前行的第2个单元格,选中单元格后滚动条将自动移动到当前行位置
    //第1个单元格属于id列,不显示,强行赋值程序将跳出
    viewRow.Selected = true;
    dataGridView_Meters.CurrentCell = viewRow.Cells[1];
    //每行恢复默认背景色、字体颜色,然后将当前行颜色改变
    foreach (DataGridViewRow row in dataGridView_Meters.Rows)
    {
        row.DefaultCellStyle.BackColor = Color.White;
        row.DefaultCellStyle.ForeColor = Color.Black;
    }
    viewRow.DefaultCellStyle.BackColor = Color.Orange;
    viewRow.DefaultCellStyle.ForeColor = Color.White;
}

40、Thread.Sleep等待的替代方法

注意:假如是在UI线程内,依然会使界面无响应

// 设置启动进程的延迟时间
int delayMilliseconds = 500;
Task.Delay(delayMilliseconds).Wait(); // 使用Task.Delay实现延迟效果

41、OpenFileDialog的ShowHelp属性会影响对话框的样式

ShowHelp属性

当ShowHelp属性为true时


ShowHelp属性为true

当ShowHelp属性为false时


ShowHelp属性为false
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容