一、知识来源
二、函数中,属性和字段的区别
1、字段(Field)是类或结构体中的成员变量。它们通常用于存储对象的状态和数据。字段可以是公共的(public)、私有的(private)或受保护的(protected),具体取决于其访问修饰符。字段可以直接在类或结构体中使用,并且可以直接访问和修改它们的值。
2、属性(Property)是一种特殊的类成员,它提供了对私有字段的访问和操作的公共接口。属性通常用于封装私有字段,以便在外部代码中访问和修改它们。属性有一个或多个访问器(getter和setter),通过这些访问器可以控制对属性的读取和写入操作。访问器可以添加额外的逻辑,例如验证输入值的有效性、触发事件等。属性也可以具有不同的访问修饰符,以控制对属性的可见性。
3、区别
- 属性是用于访问类的成员的特殊成员。实际上是方法,只是在语法上看起来像字段一样。
- 使用字段直接访问和修改数据,而属性提供了更高层次的封装和访问控制(get,set方法)。
- 字段通常用于存储对象的状态和数据,而属性则用于对外部代码提供统一的访问接口。
- 属性可以在其访问器中添加额外的逻辑,例如验证和处理输入值,触发事件等。
- 对字段的访问没有副作用,而对属性的访问可能会触发属性访问器中定义的逻辑(自定义的get、set方法)。
- 在使用时,如果你只需要简单地存储和访问数据,可以使用字段。如果你需要对数据进行封装、验证或添加其他逻辑,则应该使用属性。属性可以提供更灵活和可控的方式来访问和修改数据。
三、闭包
1、闭包是指可以访问其自身范围之外的变量的函数。这意味着一个闭包可以访问定义在其外部作用域的变量。
- 在C#中,闭包通常是通过使用匿名方法或Lambda表达式来实现的。
2、优点 - 可以访问外部作用域的变量,使得代码更加灵活和功能强大。
- 可以捕获局部变量的状态,使得函数可以记住其创建时的上下文信息。
- 可以用于创建延迟执行的代码块,方便异步编程和事件处理。
3、缺点 - 可能导致内存泄漏,因为闭包会持有对外部作用域中变量的引用,如果闭包的生命周期比外部作用域中的变量更长,就可能导致内存泄漏。
- 可能导致代码可读性和维护性下降,因为闭包可以访问外部作用域的变量,导致代码的数据流向变得不清晰。
四、通过泛型方法修改字段值
1、代码
var demo = new Demo();
demo.ProValue = 100;
demo.FielValue = 100;
// 修改字段值
ModifyFieldValue(ref demo.ProValue, 199); // 报错,属性或索引器不能作为 ref 或 out参数传递
ModifyFieldValue(ref demo.FielValue, 199);
void ModifyFieldValue<T>(ref T field, T newValue)
{
field = newValue;
}
Console.WriteLine(demo.FielValue);
class Demo
{
public int ProValue { get; set; } // 属性
public int FielValue; // 字段
}
2、在C#中,为什么属性和索引器不能作为ref或out的参数
- 属性和索引器是用于访问类的成员的特殊成员。实际上是方法,只是在语法上看起来像字段一样。对属性赋值底层是调用了set方法。
- 属性和索引器并不是存储数据的位置,而是一种用于访问和操作数据的方法。它们通常会返回一个值,而不是修改传入的参数。
- 属性和索引器的使用方式是通过类的实例或者类本身来访问的,而不是直接传递给方法作为参数。
五、通过委托参数修改属性值
1、改造二中的代码,使得能够通过通用函数修改属性值
2、代码
var demo = new Demo();
demo.ProValue = 100;
demo.FielValue = 100;
// 修改字段、属性值
ModifyPropValueWithDelegate(val => demo.ProValue = val, 42);
ModifyPropValueWithDelegate(val => demo.FielValue = val, 88);
Console.WriteLine(demo.ProValue);
Console.WriteLine(demo.FielValue);
void ModifyPropValueWithDelegate<T>(Action<T> func,T newValue)
{
func.Invoke(newValue);
}
class Demo
{
public int ProValue { get; set; } // 属性
public int FielValue; // 字段
}
3、解析
- 这里使用了闭包。闭包有内存泄漏风险。
- 使用起来局限性也比较大。
六、用反射 修改字段、属性值
1、代码
using System.Reflection;
var demo = new Demo();
demo.ProValue = 100;
demo.FielValue = 100;
// 反射 修改字段、属性值
var method = typeof(Demo).GetProperty("ProValue")!.GetSetMethod();
ModifyPropValueWithReflection(method, demo, 42);
void ModifyPropValueWithReflection<TClass,TProp>(MethodInfo method,TClass target, TProp newValue)
{
method.Invoke(target, new object?[] { newValue });
}
Console.WriteLine(demo.ProValue);
class Demo
{
public int ProValue { get; set; } // 属性
public int FielValue; // 字段
}
2、解析
-
通过typeof(Demo)获取到Demo类的Type对象
-
然后调用GetProperty("ProValue")方法获取到名为"ProValue"的属性(ProValue)对应的PropertyInfo对象。
-
使用GetSetMethod()方法获取到该属性的Set方法对应的MethodInfo对象。
ModifyPropValueWithReflection方法,method表示要修改的属性的Set方法的MethodInfo对象,target表示要修改的对象,newValue表示要设置的新值。
这段代码利用反射机制获取到Demo类中名为"ProValue"的属性的Set方法,然后通过反射调用该方法来修改对象的属性值。
七、表达式 修改字段、属性值
1、通过反射的方式编码起来比较费劲
2、代码
using System.Linq.Expressions;
using System.Reflection;
var demo = new Demo();
demo.ProValue = 100;
demo.FielValue = 100;
// 表达树 修改字段、属性值
ModifyPropValueWithExpression(d=>d.ProValue,42, demo);
void ModifyPropValueWithExpression<TClass,TProp>(Expression<Func<TClass, TProp>> expression, TProp newValue, TClass target)
{
var body = (MemberExpression)expression.Body;
var prop = (PropertyInfo)body.Member;
var setMethod = prop.GetSetMethod();
setMethod.Invoke(target, new object?[] { newValue });
}
Console.WriteLine(demo.ProValue);
class Demo
{
public int ProValue { get; set; } // 属性
public int FielValue; // 字段
}