सी # ऑपरेटर ओवरलोड के लिए `+ =`?


114

मैं ऑपरेटर अधिभार के लिए करने की कोशिश कर रहा हूँ +=, लेकिन मैं नहीं कर सकता। मैं केवल एक ऑपरेटर के लिए अधिभार बना सकता हूं +

कैसे?

संपादित करें

यह काम नहीं कर रहा है इसका कारण यह है कि मेरे पास एक वेक्टर वर्ग है (एक्स और वाई क्षेत्र के साथ)। निम्नलिखित उदाहरण पर विचार करें।

vector1 += vector2;

यदि मेरा ऑपरेटर अधिभार निम्न पर सेट है:

public static Vector operator +(Vector left, Vector right)
{
    return new Vector(right.x + left.x, right.y + left.y);
}

तो परिणाम वेक्टर 1 में जोड़ा नहीं जाएगा, लेकिन इसके बजाय, वेक्टर 1 संदर्भ के रूप में एक नया वेक्टर बन जाएगा।


2
ऐसा लगता है कि इस बारे में एक लंबी चर्चा पहले ही की जा चुकी है: maurits.wordpress.com/2006/11/27/…
क्रिस एस

39
क्या आप बता सकते हैं कि आप ऐसा करने की कोशिश क्यों कर रहे हैं? जब आप "+" को ओवरलोड करते हैं, तो आपको एक ओवरलोडेड "+ =" ऑपरेटर मुफ्त में मिल जाता है। क्या कुछ ऐसी स्थिति है जिसमें आप चाहते हैं कि "+ =" ओवरलोड हो जाए लेकिन नहीं चाहते कि "+" ओवरलोड हो?
एरिक लिपर्ट

3
C ++ से आ रहा है, यह सिर्फ गलत लगता है, लेकिन C # में यह वास्तव में सही समझ में आता है।
जॉन पूर्डी


12
@ माथियास: अपने अपडेट को फिर से करें: वैक्टर को अपरिवर्तनीय गणितीय वस्तुओं की तरह व्यवहार करना चाहिए। जब आप 2 से 3 जोड़ते हैं, तो आप ऑब्जेक्ट 3 को ऑब्जेक्ट 5 में म्यूट नहीं करते हैं। आप एक पूरी तरह से नई ऑब्जेक्ट बनाते हैं, 5. इसके अलावा ऑपरेटरों को ओवरलोड करने की बात यह है कि आप अपनी गणितीय वस्तुएं बना सकते हैं; उस उद्देश्य के विरुद्ध उन्हें परस्पर कार्य करना। मैं आपके वेक्टर प्रकार को एक अपरिवर्तनीय मूल्य प्रकार बनाऊंगा।
एरिक लिपर्ट

जवाबों:


147

MSDN से अधिभार संचालक :

असाइनमेंट ऑपरेटरों को ओवरलोड नहीं किया जा सकता है, लेकिन +=, उदाहरण के लिए, का उपयोग करके मूल्यांकन किया जाता है +, जिसे ओवरलोड किया जा सकता है।

इससे भी अधिक, असाइनमेंट ऑपरेटरों में से कोई भी ओवरलोड नहीं हो सकता है। मुझे लगता है कि यह इसलिए है क्योंकि कचरा संग्रह और मेमोरी प्रबंधन के लिए एक प्रभाव होगा, जो सीएलआर मजबूत टाइप की दुनिया में एक संभावित सुरक्षा छेद है।

फिर भी, आइए देखें कि वास्तव में एक ऑपरेटर क्या है। प्रसिद्ध जेफरी रिक्टर की पुस्तक के अनुसार , प्रत्येक प्रोग्रामिंग भाषा की अपनी ऑपरेटरों की सूची होती है, जिन्हें एक विशेष विधि कॉल में संकलित किया जाता है, और सीएलआर स्वयं ऑपरेटरों के बारे में कुछ भी नहीं जानता है। तो आइए देखते हैं कि ऑपरेटरों +और +=ऑपरेटरों के पीछे क्या रहता है ।

देखें यह सरल कोड:

Decimal d = 10M;
d = d + 10M;
Console.WriteLine(d);

आइए इस निर्देश के लिए IL-code देखें:

  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-code:

  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 कहता है, आपके पास वास्तव में एक अपरिवर्तनीय वस्तु के रूप में वैक्टर होना चाहिए। दो वैक्टरों को जोड़ने का परिणाम एक नया वेक्टर है, न कि विभिन्न आकारों वाला पहला।

यदि, किसी कारण से आपको पहले वेक्टर को बदलने की आवश्यकता है, तो आप इस अधिभार का उपयोग कर सकते हैं (लेकिन मेरे लिए, यह बहुत ही अजीब व्यवहार है):

public static Vector operator +(Vector left, Vector right)
{
    left.x += right.x;
    left.y += right.y;
    return left;
}

2
यह कहते हुए कि यह मामला जवाब नहीं दे रहा है।
जोके वैन डेर मास

1
@Jouke van der Maas और आप मुझे कैसे जवाब देना चाहते हैं यह संभव क्यों नहीं है? यह डिजाइन से असंभव है, मैं और क्या कह सकता हूं?
VMAtm

2
उन्होंने इसे इस तरह से डिजाइन क्यों किया; वास्तव में यही सवाल है। देखिए कुछ और जवाब।
ज्यूके वैन डेर मास

2
"अजीब व्यवहार" केवल अगर आप C #: p में "जन्म" प्रोग्रामिंग कर चुके हैं। लेकिन, चूंकि उत्तर सही है और बहुत अच्छी तरह से समझाया गया है, आप मेरे +1 को प्राप्त करें, साथ ही;)
थंडरग्रो

5
@ThunderGr नहीं, यह बहुत अजीब बात है कि आप किस भाषा में देख रहे हैं। बयान v3 = v1 + v2;परिणाम को v1बदलने के लिए और साथ ही v3असामान्य है
अस्सिमिलाटर

17

मुझे लगता है कि आप इस लिंक को जानकारीपूर्ण पाएंगे: अधिभार संचालक

असाइनमेंट ऑपरेटर्स को ओवरलोड नहीं किया जा सकता है, लेकिन + =, उदाहरण के लिए, + का उपयोग करके मूल्यांकन किया जाता है, जिसे ओवरलोड किया जा सकता है।


2
@pickypg - यह टिप्पणी इस थ्रेड से संबंधित नहीं है: कृपया अपने उत्तर को रद्द करें , यह मेरे प्रश्न का उत्तर देता है, मुझे लगता है कि मेरे पास आपके विकल्प का उपयोग करने के अलावा कोई विकल्प नहीं है, मुझे लगा कि इसे हल करने के लिए कुछ बेहतर तरीके हैं।
बजे शिमी वेइटहैंडलर

16

यह उसी कारण से है कि असाइनमेंट ऑपरेटर को ओवरलोड नहीं किया जा सकता है। आप ऐसा कोड नहीं लिख सकते जो असाइनमेंट को सही तरीके से पूरा करे।

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


16

आप ओवरलोड नहीं कर सकते, +=क्योंकि यह वास्तव में एक अद्वितीय ऑपरेटर नहीं है, यह सिर्फ सिंथेटिक चीनी हैx += yलिखने का एक छोटा तरीका है x = x + y। क्योंकि +=के संदर्भ में परिभाषित किया गया है +और =ऑपरेटरों, आप इसे अलग से ओवरराइड करने के लिए ऐसे मामलों में जहां में समस्या पैदा कर सकता है, की अनुमति देता है x += yऔर x = x + yठीक उसी तरह से व्यवहार नहीं किया।

निचले स्तर पर, यह बहुत संभावना है कि सी # संकलक दोनों अभिव्यक्तियों को एक ही बायटेकोड के नीचे संकलित करता है, जिसका अर्थ है कि यह बहुत संभावना है कि रनटाइम उन्हें प्रोग्राम निष्पादन के दौरान अलग तरीके से व्यवहार नहीं कर सकता है

मैं यह समझ सकता हूं कि आप इसे एक अलग ऑपरेशन की तरह x += 10मान सकते हैं : जैसे कि आप जानते हैं कि आप xऑब्जेक्ट को जगह में बदल सकते हैं और x + 10पुराने संदर्भ में इसे निर्दिष्ट करने से पहले एक नया ऑब्जेक्ट बनाने के बजाय कुछ समय / मेमोरी बचा सकते हैं। ।

लेकिन इस कोड पर विचार करें:

a = ...
b = a;
a += 10;

a == bअंत में होना चाहिए ? अधिकांश प्रकारों के लिए, नहीं, a10 से अधिक है b। लेकिन अगर आप +=ऑपरेटर को जगह पर म्यूट करने के लिए ओवरलोड कर सकते हैं , तो हाँ। अब इस पर विचार करें aऔर bकार्यक्रम के सुदूर हिस्सों के लिए पास हो सकते हैं। यदि आपका ऑब्जेक्ट उस स्थान को बदलना शुरू कर देता है जहां आपकी ऑब्जेक्ट बदलना शुरू करती है, तो कोड इसकी अपेक्षा नहीं करता है।

दूसरे शब्दों में, यदि प्रदर्शन इतना महत्वपूर्ण है, तो इसे x += 10कॉल करने के तरीके के साथ बदलना बहुत मुश्किल नहीं है x.increaseBy(10), और इसमें शामिल सभी लोगों के लिए यह बहुत स्पष्ट है।


2
व्यक्तिगत रूप से, मैं बदल जाएगा it's just syntactic sugarकरने के लिए it's just syntactic sugar in C#; अन्यथा, यह बहुत सामान्य लगता है, लेकिन कुछ प्रोग्रामिंग भाषाओं में, यह न केवल सिंटैक्टिक चीनी है, बल्कि वास्तव में प्रदर्शन लाभ दे सकता है।
सेबेस्टियन मच

सरल (इंट, फ्लोट, आदि) प्रकारों के लिए, +=संभवतः एक स्मार्ट कंपाइलर द्वारा अनुकूलित किया जा सकता है, क्योंकि अंकगणितीय सरल है। लेकिन एक बार जब आप वस्तुओं के साथ काम कर रहे होते हैं, तो सभी दांव बंद हो जाते हैं। कोई भी भाषा बहुत ही समस्याओं का सामना करती है। यही कारण है कि ऑपरेटर ओवरलोडिंग बुराई है।
बेन्जादो

@SebastianMach प्रश्न को विशेष रूप से c # और dotnet टैग के साथ टैग किया गया है। जाहिर है, उदाहरण के लिए c ++ में, '+' और '+ =' (और यहां तक ​​कि '=') को अलग-अलग ओवरलोड किया जा सकता है।
बोजिदर स्टेंचव

1
@BojidarStanchev: यह सच है। मैं अपने 9 साल के छोटे स्वयं से माफी माँगता हूँ:
सेबेस्टियन मच

9

इसका कारण यह है कि इस ऑपरेटर को अतिभारित नहीं किया जा सकता है:

असाइनमेंट ऑपरेटर्स को ओवरलोड नहीं किया जा सकता है, लेकिन + =, उदाहरण के लिए, + का उपयोग करके मूल्यांकन किया जाता है, जिसे ओवरलोड किया जा सकता है।

MSDN

बस ओवरलोड +ऑपरेटर, की वजह से

x += y के बराबर x = x + y


6

ऑपरेटर अधिभार के लिए ऑपरेटर +में उपयोग किया जाता है +=, के A += Bबराबर होता है A = operator+(A, B)


6

यदि आप +ऑपरेटर को इस तरह से ओवरलोड करते हैं:

class Foo
{
    public static Foo operator + (Foo c1, int x)
    {
        // implementation
    }
}

तुम कर सकते हो

 Foo foo = new Foo();
 foo += 10;

या

 foo = foo + 10;

यह संकलन और समान रूप से चलेगा।


6

इस समस्या का हमेशा एक ही जवाब होता है: आपको जरूरत क्यों है +=, अगर आप इसे ओवरलोड करते हैं तो आपको मुफ्त में मिलेगा +। लेकिन अगर मेरे पास इस तरह एक वर्ग है तो क्या होगा।

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 # में उच्च-प्रदर्शन कंप्यूटिंग करने की कोशिश करते हैं, तो प्रसंस्करण समय और मेमोरी की खपत को कम करने के लिए आपके पास ऐसी विशेषताओं की आवश्यकता है, अगर किसी के पास अच्छा समाधान है तो इसकी बहुत सराहना की जाती है, लेकिन मुझे यह मत बताओ कि मुझे स्थैतिक तरीकों से ऐसा करना होगा , यह केवल एक वर्कअराउंड है और मुझे कोई कारण नहीं दिखता है कि C # +=कार्यान्वयन क्यों करता है यदि इसे परिभाषित नहीं किया गया है, और यदि इसे परिभाषित किया गया है तो इसका उपयोग किया जाएगा। कुछ लोग कहते हैं कि त्रुटियों के बीच अंतर नहीं है +और +=रोकता है, लेकिन क्या यह मेरी अपनी समस्या नहीं है?


2
यदि आप वास्तव में प्रदर्शन के बारे में परवाह करते हैं, तो आप ऑपरेटर को ओवरलोडिंग के साथ गड़बड़ करने वाले नहीं हैं, जो केवल यह बताना कठिन बनाता है कि किस कोड को लागू किया जा रहा है। इस बात के लिए कि क्या +=आपके शब्दार्थ को गड़बड़ाना आपकी अपनी समस्या है ... यह केवल तभी सच है जब किसी और को आपके कोड को पढ़ना, रखरखाव करना या निष्पादित नहीं करना है।
बेन्जादो

2
हैलो, बेंज़ादो। किसी तरह से आप सही हैं, लेकिन हमारे पास क्या है जो प्रोटोटाइप अनुप्रयोगों को बनाने के लिए उच्च प्रदर्शन कंप्यूटिंग के लिए एक मंच है। एक तरह से हमें प्रदर्शन की आवश्यकता है, दूसरी तरफ हमें एक सरल शब्दार्थ की आवश्यकता है। वास्तव में हम C # की तुलना में अधिक ऑपरेटरों को रखना पसंद करते हैं वर्तमान में वितरित करते हैं। यहाँ मैं C # 5 के लिए उम्मीद कर रहा हूँ और सी # भाषा से अधिक प्राप्त करने के लिए एक सेवा तकनीक के रूप में संकलक। फिर भी जब तक मैं C ++ के साथ बड़ा हुआ और सराहना करता हूं कि अगर C # में C ++ से थोड़ी अधिक विशेषताएँ हैं, तो मैं I # m C C करने के बाद से C ++ को दोबारा नहीं छूना चाहूंगा।
मसिदी

2
इंजीनियरिंग सभी ट्रेडऑफ के बारे में है; आप जो चाहते हैं वह सब कुछ एक कीमत के साथ आता है।
बेन्जादो

3
अंकगणित संचालक सम्मेलन द्वारा नए उदाहरण लौटाते हैं - इसलिए उन्हें आमतौर पर अपरिवर्तनीय प्रकारों पर ओवरराइड किया जाता है। आप उदाहरण के लिए, List<T>ऑपरेटर का उपयोग करके एक नया तत्व नहीं जोड़ सकते list += "new item"। आप इसके Addबजाय इसकी विधि कहते हैं ।
फ़ाक़ गुर डेस


0

एक बेहतर डिजाइन विधि स्पष्ट कास्टिंग है। आप निश्चित रूप से कास्टिंग अधिभार कर सकते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.