[英]C# operator overload for `+=`?
我正在嘗試為+=
執行運算符重載,但我不能。 我只能為+
做一個運算符重載。
怎么來的?
編輯
這不起作用的原因是我有一個 Vector class (帶有 X 和 Y 字段)。 考慮以下示例。
vector1 += vector2;
如果我的運算符重載設置為:
public static Vector operator +(Vector left, Vector right)
{
return new Vector(right.x + left.x, right.y + left.y);
}
然后結果不會被添加到vector1,而是vector1也將通過引用成為一個全新的Vector。
可重載的運算符,來自 MSDN:
賦值運算符不能被重載,但是
+=
,例如,使用+
來評估,它可以被重載。
更重要的是,沒有一個賦值運算符可以重載。 我認為這是因為垃圾收集和 memory 管理會產生影響,這是 CLR 強類型世界中潛在的安全漏洞。
不過,讓我們看看運算符到底是什么。 根據著名的 Jeffrey Richter 的書,每種編程語言都有自己的運算符列表,這些運算符列表是在特殊的方法調用中編譯的,而 CLR 本身對運算符一無所知。 因此,讓我們看看+
和+=
運算符后面到底有什么。
看這個簡單的代碼:
Decimal d = 10M;
d = d + 10M;
Console.WriteLine(d);
讓我們查看此指令的 IL 代碼:
IL_0000: nop
IL_0001: ldc.i4.s 10
IL_0003: newobj instance void [mscorlib]System.Decimal::.ctor(int32)
IL_0008: stloc.0
IL_0009: ldloc.0
IL_000a: ldc.i4.s 10
IL_000c: newobj instance void [mscorlib]System.Decimal::.ctor(int32)
IL_0011: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Addition(valuetype [mscorlib]System.Decimal,
valuetype [mscorlib]System.Decimal)
IL_0016: stloc.0
現在讓我們看看這段代碼:
Decimal d1 = 10M;
d1 += 10M;
Console.WriteLine(d1);
以及為此的 IL 代碼:
IL_0000: nop
IL_0001: ldc.i4.s 10
IL_0003: newobj instance void [mscorlib]System.Decimal::.ctor(int32)
IL_0008: stloc.0
IL_0009: ldloc.0
IL_000a: ldc.i4.s 10
IL_000c: newobj instance void [mscorlib]System.Decimal::.ctor(int32)
IL_0011: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Addition(valuetype [mscorlib]System.Decimal,
valuetype [mscorlib]System.Decimal)
IL_0016: stloc.0
他們是平等的! 因此+=
運算符只是C# 中程序的語法糖,您可以簡單地重載+
運算符。
例如:
class Foo
{
private int c1;
public Foo(int c11)
{
c1 = c11;
}
public static Foo operator +(Foo c1, Foo x)
{
return new Foo(c1.c1 + x.c1);
}
}
static void Main(string[] args)
{
Foo d1 = new Foo (10);
Foo d2 = new Foo(11);
d2 += d1;
}
此代碼將被編譯並成功運行為:
IL_0000: nop
IL_0001: ldc.i4.s 10
IL_0003: newobj instance void ConsoleApplication2.Program/Foo::.ctor(int32)
IL_0008: stloc.0
IL_0009: ldc.i4.s 11
IL_000b: newobj instance void ConsoleApplication2.Program/Foo::.ctor(int32)
IL_0010: stloc.1
IL_0011: ldloc.1
IL_0012: ldloc.0
IL_0013: call class ConsoleApplication2.Program/Foo ConsoleApplication2.Program/Foo::op_Addition(class ConsoleApplication2.Program/Foo,
class ConsoleApplication2.Program/Foo)
IL_0018: stloc.1
更新:
根據您的更新 - 正如@EricLippert 所說,您確實應該將向量作為不可變的 object。 兩個向量相加的結果是一個新向量,而不是第一個具有不同大小的向量。
如果由於某種原因需要更改第一個向量,則可以使用此重載(但對我而言,這是非常奇怪的行為):
public static Vector operator +(Vector left, Vector right)
{
left.x += right.x;
left.y += right.y;
return left;
}
我想你會發現這個鏈接信息豐富: Overloadable Operators
賦值運算符不能重載,但 +=,例如,使用 + 計算,可以重載。
你不能重載+=
因為它不是一個真正的唯一運算符,它只是語法糖。 x += y
只是x = x + y
的一種簡寫方式。 因為+=
是根據+
和=
運算符定義的,所以在x += y
和x = x + y
的行為方式不完全相同的情況下,允許您單獨覆蓋它可能會產生問題。
在較低級別,C# 編譯器很可能將兩個表達式編譯為相同的字節碼,這意味着運行時很可能在程序執行期間無法區別對待它們。
我可以理解您可能希望將其視為單獨的操作:在x += 10
之類的語句中,您知道您可以在適當的位置改變x
object 並可能節省一些時間/內存,而不是創建新的 object x + 10
在將其分配給舊參考之前。
但是考慮一下這段代碼:
a = ...
b = a;
a += 10;
最后應該a == b
嗎? 對於大多數類型,不, a
比b
多 10。 但是,如果您可以重載+=
運算符以進行適當的變異,那么可以。 現在考慮a
和b
可以傳遞到程序的遙遠部分。 如果您的 object 開始更改代碼不期望的位置,您可能的優化可能會產生令人困惑的錯誤。
換句話說,如果性能如此重要,那么將x += 10
替換為x.increaseBy(10)
之類的方法調用並不難,而且對所有相關人員來說都更加清晰。
這與賦值運算符不能重載的原因相同。 您無法編寫能夠正確執行分配的代碼。
class Foo
{
// Won't compile.
public static Foo operator= (Foo c1, int x)
{
// duh... what do I do here? I can't change the reference of c1.
}
}
賦值運算符不能重載,但 +=,例如,使用 + 計算,可以重載。
來自MSDN 。
+ 運算符重載用於+
+=
運算符, A += B
等於A = operator+(A, B)
。
如果像這樣重載+
運算符:
class Foo
{
public static Foo operator + (Foo c1, int x)
{
// implementation
}
}
你可以做
Foo foo = new Foo();
foo += 10;
或者
foo = foo + 10;
這將同樣編譯和運行。
這個問題總是有相同的答案:為什么你需要+=
,如果你超載+
是免費的。 但是如果我有這樣的 class 會發生什么。
using System;
using System.IO;
public class Class1
{
public class MappableObject
{
FileStream stream;
public int Blocks;
public int BlockSize;
public MappableObject(string FileName, int Blocks_in, int BlockSize_in)
{
Blocks = Blocks_in;
BlockSize = BlockSize_in;
// Just create the file here and set the size
stream = new FileStream(FileName); // Here we need more params of course to create a file.
stream.SetLength(sizeof(float) * Blocks * BlockSize);
}
public float[] GetBlock(int BlockNo)
{
long BlockPos = BlockNo * BlockSize;
stream.Position = BlockPos;
using (BinaryReader reader = new BinaryReader(stream))
{
float[] resData = new float[BlockSize];
for (int i = 0; i < BlockSize; i++)
{
// This line is stupid enough for accessing files a lot and the data is large
// Maybe someone has an idea to make this faster? I tried a lot and this is the simplest solution
// for illustration.
resData[i] = reader.ReadSingle();
}
}
retuen resData;
}
public void SetBlock(int BlockNo, float[] data)
{
long BlockPos = BlockNo * BlockSize;
stream.Position = BlockPos;
using (BinaryWriter reader = new BinaryWriter(stream))
{
for (int i = 0; i < BlockSize; i++)
{
// Also this line is stupid enough for accessing files a lot and the data is large
reader.Write(data[i];
}
}
retuen resData;
}
// For adding two MappableObjects
public static MappableObject operator +(MappableObject A, Mappableobject B)
{
// Of course we have to make sure that all dimensions are correct.
MappableObject result = new MappableObject(Path.GetTempFileName(), A.Blocks, A.BlockSize);
for (int i = 0; i < Blocks; i++)
{
float[] dataA = A.GetBlock(i);
float[] dataB = B.GetBlock(i);
float[] C = new float[dataA.Length];
for (int j = 0; j < BlockSize; j++)
{
C[j] = A[j] + B[j];
}
result.SetBlock(i, C);
}
}
// For adding a single float to the whole data.
public static MappableObject operator +(MappableObject A, float B)
{
// Of course we have to make sure that all dimensions are correct.
MappableObject result = new MappableObject(Path.GetTempFileName(), A.Blocks, A.BlockSize);
for (int i = 0; i < Blocks; i++)
{
float[] dataA = A.GetBlock(i);
float[] C = new float[dataA.Length];
for (int j = 0; j < BlockSize; j++)
{
C[j] = A[j] + B;
}
result.SetBlock(i, C);
}
}
// Of course this doesn't work, but maybe you can see the effect here.
// when the += is automimplemented from the definition above I have to create another large
// object which causes a loss of memory and also takes more time because of the operation -> altgough its
// simple in the example, but in reality it's much more complex.
public static MappableObject operator +=(MappableObject A, float B)
{
// Of course we have to make sure that all dimensions are correct.
MappableObject result = new MappableObject(Path.GetTempFileName(), A.Blocks, A.BlockSize);
for (int i = 0; i < Blocks; i++)
{
float[] dataA = A.GetBlock(i);
for (int j = 0; j < BlockSize; j++)
{
A[j]+= + B;
}
result.SetBlock(i, A);
}
}
}
}
您是否仍然說+=
是“自動實現的”很好。 如果您嘗試在 C# 中進行高性能計算,您需要具有這樣的功能來減少處理時間和 memory 消耗,如果有人有一個好的解決方案,非常感謝,但不要告訴我我必須使用 ZA81259CEF45659C2947EDF1 方法來做到這一點,這只是一種解決方法,我看不出為什么 C# 沒有定義+=
實現,如果它被定義,它將被使用。 有人說+
和+=
之間沒有區別可以防止錯誤,但這不是我自己的問題嗎?
我有完全相同的問題,我不可能比這個人回答得更好
更好的設計方法是顯式鑄造。 您絕對可以重載 Casting。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.