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安全设置”
二、修改清单文件
上一步操作后,项目下面会多出一个名为“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安全设置”前面的勾去掉后再编译运行,不然程序会报错无法运行。
四、重新编译运行程序
打开程序时,会提示“用户账户控制”来获取管理员权限运行,点击“是”获取管理员权限。
20、使Windows服务具有足够的权限
将服务的启动账户设置为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 表达式,该表达式采用值 String 和 Int32 值作为参数
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文档注释
- 换行时添加<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 窗体错误处理,请添加以下设置:
-
在 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 的“工具”>“选项”(或“调试”>“选项”)对话框中配置实时调试。
启用或禁用实时调试:
-
在“工具”或“调试”菜单上,选择“选项”>“调试”>“实时”。
如果未显示“实时”菜单选项,请确保使用 Visual Studio 安装程序安装了实时调试程序。 在“启用这些代码类型的实时调试”框中,选择希望通过实时调试进行调试的代码类型:“托管”、“本机”和/或“脚本”。
选择“确定”。
如果启用了实时调试器,但它并未在应用程序崩溃或出错时打开,请参阅实时调试疑难解答。
3) 从 Windows 注册表禁用实时调试
即便计算机中不再安装有 Visual Studio,仍可启用实时调试。 如果不再安装 Visual Studio,则可以通过编辑 Windows 注册表来禁用实时调试。
若要通过编辑注册表禁用实时调试,请执行以下操作:
- 从 Windows“开始”菜单,运行“注册表编辑器”(regedit.exe) 。
- 在“注册表编辑器”窗口中,找到并删除下列注册表项(如果存在):
- HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft.NETFramework\DbgManagedDebugger
-
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\AeDebug\Debugger
- 如果存在以下注册表项,请将其删除:
- HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft.NETFramework\DbgManagedDebugger
- HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug\Debugger
确保不要删除或更改任何其他注册表项。
- 关闭“注册表编辑器”窗口。
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(字符串值)
List of excluded applications
2) 效果
2.1) DontShowUI值为0时
注意:在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
-
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属性为true时
当ShowHelp属性为false时