7

我读了这个答案:https ://stackoverflow.com/a/9928643/16241

但我显然不明白,因为我无法弄清楚为什么我的方法不纯。(有问题的方法是ToExactLocation())。

public struct ScreenLocation
{
    public ScreenLocation(int x, int y):this()
    {
        X = x;
        Y = y;
    }

    public int X { get; set; }
    public int Y { get; set; }

    public ExactLocation ToExactLocation()
    {
        return new ExactLocation {X = this.X, Y = this.Y};
    }

    // Other stuff
}

如果您需要它,这里是确切的位置结构:

public struct ExactLocation
{
    public double X { get; set; }
    public double Y { get; set; }

    // Various Operator Overloads, but no constructor
}

这就是我所说的:

someScreenLocation = MethodThatGivesAScreenLocation();
if (DestinationLocation == someScreenLocation.ToExactLocation())
{
     // Do stuff
}

当我这样做时,ReSharper 将其标记为"Impure Method is called for readonly field of value type."

为什么这么说?我该怎么做才能让它消失?

4

6 回答 6

9

它不是纯粹的,因为它不会返回仅取决于其输入的值。当X或的值Y改变时, 的返回值也随之改变ToExactLocation,即它的输出取决于内部的可变状态。

此外,设置器 forXYinExactLocation 可能会改变输入。可能的吸气剂ScreenLocation

someScreenLocation是一个只读字段并且是一个值类型。您正在调用ToExactLocation一个值,即只读字段。当您访问只读值类型时,会创建一个副本以避免改变值本身。但是,您的调用可能会改变该值,在许多情况下,这不是您想要的,因为您将改变一个副本。这就是您收到警告的原因。

在这种情况下,您可以忽略它,但我通常会避免使用可变值类型。

编辑:

让我尝试简化...

struct Point
{
    int X;
    int Y;
    bool Mutate() { X++; Y++; }
}

class Foo
{
    public readonly Point P;
    Foo() 
    { 
        P = new Point();
        P.Mutate();  // impure function on readonly value type
    }
}

Mutate()被调用时,会创建一个副本P并与该方法一起传递。的内部状态的任何突变都P将是无关紧要的,因为它会突变一个副本。

于 2013-03-25T03:46:04.793 回答
6

纯方法的条件之一是其输出(返回值)完全取决于其输入(参数)。

您的.ToExactLocation()方法不是纯粹的,因为它的输出既取决于输入参数,也取决于可变结构的当前值。

Resharper 不喜欢这样,因为可变结构很糟糕(不要使用它们)。我希望如果您将代码更改为使用类而不是结构或重新设计结构以便 .X 和 .Y 成员只能由构造函数设置,则错误会消失。

于 2013-03-25T03:45:51.687 回答
4

阅读答案,我发现pure函数必然像数学中的函数。如果为 0,f(x) = x^2 + 2x + 10始终返回。10x

因此ToExactLocation(),每次调用它时都必须返回相同的值,而不管对象自初始创建以来发生的变化,才能将其称为“纯”。

于 2013-03-25T03:46:03.613 回答
2

“纯函数”有两种含义:一种是理论上的的(无副作用/不依赖于可变状态),另一种是 ReSharper 对函数的看法。

从理论的角度来看,您的函数不是纯粹的,因为它依赖于可变状态。样本:

var someScreenLocation = new ScreenLocation(1,1);

var locationOne = someScreenLocation.ToExactLocation();
var locationTwo = someScreenLocation.ToExactLocation();
someScreenLocation.X = 3;
var locationThree = someScreenLocation.ToExactLocation();

对于纯粹的方法,它只能根据输入来更改其结果(根本不像在这种情况下,因为没有参数)。但是您可以清楚地观察到locationOne并且locationTwo是相同的(到目前为止是好兆头),但不幸的是locationThree的是即使输入(函数的参数)仍然相同,它们也是不同的。

X您可以通过制作和Y readonly(并添加构造函数)使其理论上纯粹。

即使在更改之后,ReSharper 仍然会认为它不是纯的 - 为了说服它,您可以使用Pure属性将其标记为纯。

请注意,即使在具有readonly字段的类的构造函数中,ReSharper 也会标记“不纯”函数的使用。下面的示例显示了 ReSharper 警告:

struct Point
{
    public int X;
    public int Y;
    public Point(int x, int y){X = x;Y = y;}

    public void Mutate(){X++;}
    public Point TheoreticallyPure(){return new Point(1, 1);}
    [Pure] public Point MarkedPure(){ return new Point(1, 1);}
}

class WithReadonlyField
{
    public readonly Point P;
    public WithReadonlyField()
    {
        P = new Point();
        P.TheoreticallyPure();  // impure function on readonly value type
        P.MarkedPure(); // return value of pure not used
        P.Mutate();   // impure function on readonly value type - modifies P.
        P = new Point().MarkedPure(); // ok to modify P multiple times.
    }
    public void NormalMethod()
    {
        P.Mutate();   // impure function on readonly value type, no changes to P
    }
}

C# 允许修改readonly字段直到构造函数的末尾,但是 ReSharper 也标记了那里所有“不纯”函数的用法(请注意,Mutate构造函数中的函数实际上更改了readonlyfield的值P,这与NormalMethod它没有效果的情况不同)。

readonly ...对声明引入的字段的赋值只能作为声明的一部分或在同一类的构造函数中出现”

ReSharper 的这种行为很可能是为了保持一致性,并避免移动完全有效的代码完全改变行为的情况。

于 2013-03-25T04:03:31.160 回答
1

最好将其建模为静态方法(在任一类上)并消除不纯的警告。省略了解释,因为其他答案已经涵盖了原因。

例子:

public static ExactLocation ToExactLocation(ScreenLocation loc)
{
    return new ExactLocation {X = loc.X, Y = loc.Y};
}

或使用扩展方法

public static ExactLocation ToExactLocation(this ScreenLocation loc)
{
    return new ExactLocation {X = loc.X, Y = loc.Y};
}
于 2013-03-25T04:08:06.313 回答
0

不太确定原因,如果格式正确,我会将其作为评论...

你不想要这样的东西:

var someScreenLocation = MethodThatGivesAScreenLocation();
if (DestinationLocation.X == someScreenLocation.ToExactLocation().X &&
    DestinationLocation.Y == someScreenLocation.ToExactLocation().Y)
{
     // Do stuff
}
于 2013-03-25T03:50:02.600 回答