编写安全有效的 C# 代码

Ref 局部变量和返回结果

允许使用并返回对变量的引用的算法,这些变量在其他位置定义。 添加 ref 局部变量和 ref 返回结果可通过避免复制值或多次执行取消引用操作,允许更为高效的算法。

public static ref int Find(int[,] matrix, Func<int, bool> predicate)
{
    for (int i = 0; i < matrix.GetLength(0); i++)
        for (int j = 0; j < matrix.GetLength(1); j++)
            if (predicate(matrix[i, j]))
                return ref matrix[i, j];
    throw new InvalidOperationException("Not found");
}

ref var item = ref MatrixSearch.Find(matrix, (val) => val == 42);
Console.WriteLine(item);
item = 24;
Console.WriteLine(matrix[4, 2]);

 

声明不可变值类型的只读结构

使用 readonly 修饰符声明 struct 将通知编译器你的意图是创建不可变类型。

readonly public struct ReadonlyPoint3D
{
    public ReadonlyPoint3D(double x, double y, double z)
    {
        this.X = x;
        this.Y = y;
        this.Z = z;
    }

    public double X { get; }
    public double Y { get; }
    public double Z { get; }
}

 

结构可变时声明 readonly 成员

向不改变状态的成员添加 readonly 修饰符有两个相关的好处。 首先,编译器会强制执行你的意图。 该成员无法改变结构的状态。 其次,访问 readonly 成员时,编译器不会创建 in 参数的防御性副本。

public struct Point3D
{
    public Point3D(double x, double y, double z)
    {
        _x = x;
        _y = y;
        _z = z;
    }

    private double _x;
    public double X
    {
        readonly get => _x;
        set => _x = value;
    }

    private double _y;
    public double Y
    {
        readonly get => _y;
        set => _y = value;
    }

    private double _z;
    public double Z
    {
        readonly get => _z;
        set => _z = value;
    }

    public readonly double Distance => Math.Sqrt(X * X + Y * Y + Z * Z);

    public readonly override string ToString() => $"{X}, {Y}, {Z}";
}

 如果使用自动实现的属性,则编译器会将 readonly 修饰符添加到 get 访问器以获取读写属性。 对于仅具有 get 访问器的属性,编译器会将 readonly 修饰符添加到自动实现的属性声明中。

 请注意,readonly 修饰符对于只读属性是必需的。 编译器会假设 get 访问器可以修改状态;必须显式声明 readonly。 自动实现的属性是一个例外;编译器会将所有自动实现的 Getter 视为 readonly,因此,此处无需向 X 和 Y 属性添加 readonly 修饰符。

 

尽可能对大型结构使用 ref readonly return 语句

public struct Point3D
{
    private static Point3D origin = new Point3D(0,0,0);

    public static ref readonly Point3D Origin => ref origin;

    // other members removed for space
}

 

将 in 修饰符应用于大于 System.IntPtr.Size 的 readonly struct 参数

in 关键字补充了现有的 ref 和 out 关键字,以按引用传递参数。 in 关键字指定按引用传递参数,但调用的方法不修改值。

这种做法通常可以提高大于 IntPtr.Size 的只读值类型的性能。 对于简单类型(sbytebyteshortushortintuintlongulongcharfloatdoubledecimal 和 bool 以及 enum 类型),任何潜在的性能提升都是极小的。 实际上,对于小于 IntPtr.Size 的类型,使用按引用传递可能会降低性能。

 

避免在 in 参数中使用可变结构

private static double CalculateDistance(in Point3D point1, in Point3D point2)
{
    double xDifference = point1.X - point2.X;
    double yDifference = point1.Y - point2.Y;
    double zDifference = point1.Z - point2.Z;

    return Math.Sqrt(xDifference * xDifference + yDifference * yDifference + zDifference * zDifference);
}

 如果没有语言保证,编译器必须在调用任何未标记为 readonly 修饰符的成员之前创建参数的临时副本。这些副本会降低性能,使得按值传递比按只读引用传递速度更快。

相反,如果距离计算使用不可变结构 ReadonlyPoint3D,则不需要临时对象:

private static double CalculateDistance3(in ReadonlyPoint3D point1, in ReadonlyPoint3D point2 = default)
{
    double xDifference = point1.X - point2.X;
    double yDifference = point1.Y - point2.Y;
    double zDifference = point1.Z - point2.Z;

    return Math.Sqrt(xDifference * xDifference + yDifference * yDifference + zDifference * zDifference);
}

 

posted @ 2020-08-07 17:11  yetsen  阅读(198)  评论(0)    收藏  举报