ত্রুটি: "রিটার্ন মানটি পরিবর্তন করতে পারে না" গ #


155

আমি স্বতঃ-প্রয়োগকৃত বৈশিষ্ট্যগুলি ব্যবহার করছি। আমি অনুমান করি যে নিম্নলিখিতটি সমাধানের দ্রুততম উপায়টি কি আমার নিজের ব্যাকিং ভেরিয়েবলটি ঘোষণা করা?

public Point Origin { get; set; }

Origin.X = 10; // fails with CS1612

ত্রুটির বার্তা: 'এক্সপ্রেশন' এর রিটার্ন মানটি পরিবর্তন করতে পারে না কারণ এটি ভেরিয়েবল নয়

একটি মানের ধরণের সংশোধন করার চেষ্টা করা হয়েছিল যা একটি মধ্যবর্তী প্রকাশের ফলাফল। মান স্থায়ী না থাকায় মানটি অপরিবর্তিত থাকবে।

এই ত্রুটিটি সমাধান করার জন্য, একটি মধ্যবর্তী মানতে অভিব্যক্তির ফলাফল সংরক্ষণ করুন, বা মধ্যবর্তী ভাবের জন্য একটি রেফারেন্স টাইপ ব্যবহার করুন।


13
কেন এটি পরিবর্তনীয় মান ধরণের একটি খারাপ ধারণা হ'ল এটির আরও একটি চিত্রণ। যদি আপনি কোনও মান ধরণের পরিবর্তন এড়াতে পারেন তবে এটি করুন।
এরিক লিপার্ট

নিম্নলিখিত কোডটি ধরুন (নির্দিষ্ট ইএল :-) দ্বারা ব্লগ করা একটি আষ্টার বাস্তবায়নের সময়ে আমার প্রচেষ্টা থেকে, যা কোনও মান ধরণের পরিবর্তন এড়াতে পারে না: শ্রেণীর পথ <টি>: অনুমানযোগ্য <টি> যেখানে টি: ইনোড, নতুন () {{) { ..} সর্বজনীন HexNode (int x, int y): এটি (নতুন পয়েন্ট (x, y))} <পথ <T> পথ = নতুন পথ <টি> (নতুন টি (এক্স, y)); // ত্রুটি // কুৎসিত পথ <<> পথ = নতুন পথ <টি> (নতুন টি ()); path.LastStep.Centre = নতুন পয়েন্ট (x, y);
টম উইলসন

উত্তর:


198

এটি কারণ Pointএকটি মান প্রকার ( struct)।

এই কারণে, আপনি যখন শ্রেণীর দ্বারা পরিচালিত মানের Originএকটি অনুলিপিটি অ্যাক্সেস করে সেই সম্পত্তিটি অ্যাক্সেস করেন , কোনও রেফারেন্স টাইপ ( class) সহ আপনার নিজের মতো মান হয় না , তাই আপনি যদি Xসম্পত্তিটি সেট করেন তবে আপনি সেট করছেন অনুলিপিটিতে থাকা সম্পত্তিটি এবং তারপরে এটি বাতিল করে, মূল মানটি অপরিবর্তিত রেখে। এটি সম্ভবত আপনি যা ইচ্ছা করেছিলেন তা নয়, তাই সংকলক আপনাকে এটি সম্পর্কে সতর্ক করে দিচ্ছে।

আপনি যদি কেবল Xমানটি পরিবর্তন করতে চান তবে আপনাকে এই জাতীয় কিছু করতে হবে:

Origin = new Point(10, Origin.Y);

2
@ পল: আপনার কি কাঠামোটিকে কোনও শ্রেণিতে পরিবর্তন করার দক্ষতা আছে?
ডগ

1
এটি এক ধরণের ধোঁকাবাজি, কারণ সম্পত্তি নির্ধারক নিয়োগকারীটির একটি পার্শ্ব প্রতিক্রিয়া রয়েছে (কাঠামোটি একটি ব্যাকিং রেফারেন্স টাইপের ক্ষেত্রে দৃষ্টিভঙ্গি হিসাবে কাজ করে)
আলেকজান্ডার - মনিকা পুনরায়

আরেকটি সমাধান হ'ল আপনার স্ট্রাক্টটিকে কেবল ক্লাসে পরিণত করা। সি ++ এর বিপরীতে, যেখানে শ্রেণি এবং কাঠামো কেবলমাত্র ডিফল্ট সদস্য অ্যাক্সেসের দ্বারা পৃথক হয় (যথাক্রমে বেসরকারী এবং পাবলিক), স্ট্রাক্ট এবং সি # তে ক্লাসের আরও কয়েকটি পার্থক্য রয়েছে। এখানে আরও কিছু তথ্য দেওয়া হয়েছে: ডকস.মাইক্রোসফটকম
us

9

ব্যাকিং ভেরিয়েবল ব্যবহার করলে কোনও লাভ হবে না। Pointধরনের একটি মূল্য প্রকার।

আপনাকে মূল পয়েন্টটি পুরো পয়েন্ট মান নির্ধারণ করতে হবে: -

Origin = new Point(10, Origin.Y);

সমস্যাটি হ'ল আপনি যখন অরিজিন সম্পত্তিটি অ্যাক্সেস করেন তখন যা ফিরে আসে getতা অরিজিন বৈশিষ্ট্যগুলি স্বয়ংক্রিয়ভাবে তৈরি ক্ষেত্রের পয়েন্ট কাঠামোর অনুলিপি। সুতরাং আপনার এক্স ক্ষেত্রের পরিবর্তনটি এই অনুলিপি অন্তর্নিহিত ক্ষেত্রকে প্রভাবিত করবে না। সংকলক এটি সনাক্ত করে এবং আপনাকে একটি ত্রুটি দেয় কারণ এই ক্রিয়াটি সম্পূর্ণ অকেজো।

এমনকি যদি আপনি নিজের ব্যাকিং ভেরিয়েবল ব্যবহার করেন getতবে দেখতে আপনার চেহারাটি হ'ল: -

get { return myOrigin; }

আপনি এখনও পয়েন্ট কাঠামোর একটি অনুলিপি ফিরিয়ে আনবেন এবং আপনি একই ত্রুটি পাবেন get

হুম ... আপনার প্রশ্নটি আরও মনোযোগ সহকারে পড়ার অর্থ সম্ভবত আপনি আপনার ক্লাসের মধ্যে থেকে সরাসরি ব্যাকিং ভেরিয়েবলটি সংশোধন করতে চাইছেন: -

myOrigin.X = 10;

হ্যাঁ এটি আপনার প্রয়োজন হবে।


6

ততক্ষণে আপনি ইতিমধ্যে জানেন যে ত্রুটির উত্সটি কী। আপনার সম্পত্তি নিতে কোনও ওভারলোডের সাথে কোনও নির্মাণকারীর অস্তিত্ব না থাকলে (এই ক্ষেত্রে X) আপনি অবজেক্ট ইনিশিয়ালাইজারটি ব্যবহার করতে পারেন (যা পর্দার আড়ালে সমস্ত যাদু করবে)। আপনার স্ট্রাকগুলি অপরিবর্তনীয় করে তৈরি করার দরকার নেই এমন নয় , কেবল অতিরিক্ত তথ্য প্রদান:

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

class MyClass
{
    public Point Origin { get; set; }
}

MyClass c = new MyClass();
c.Origin.X = 23; //fails.

//but you could do:
c.Origin = new Point { X = 23, Y = c.Origin.Y }; //though you are invoking default constructor

//instead of
c.Origin = new Point(23, c.Origin.Y); //in case there is no constructor like this.

এটি সম্ভব কারণ পর্দার অন্তরালে এটি ঘটে:

Point tmp = new Point();
tmp.X = 23;
tmp.Y = Origin.Y;
c.Origin = tmp;

এটি করার মতো দেখতে খুব অদ্ভুত জিনিস বলে মনে হচ্ছে, মোটেই প্রস্তাবিত নয়। কেবল একটি বিকল্প উপায়ে তালিকাভুক্ত করা হচ্ছে। করার আরও ভাল উপায় হ'ল স্ট্রাক্টকে অপরিবর্তনীয় করে তোলা এবং একটি উপযুক্ত কনস্ট্রাক্টর সরবরাহ করা।


2
যে ট্র্যাশ এর মান খুঁজে পাবে না Origin.Y? প্রকারের সম্পত্তি হিসাবে Point, আমি ভাবব যে পরিবর্তনের idiomatic উপায় ঠিক Xহবে var temp=thing.Origin; temp.X = 23; thing.Origin = temp;। অভিব্যক্তিক পদ্ধতির সুবিধা রয়েছে যে এটি যে সদস্যদের সংশোধন করতে চায় না তা উল্লেখ করতে হবে না, এমন একটি বৈশিষ্ট্য যা কেবলমাত্র সম্ভব কারণ কারণ Pointপরিবর্তনীয়। আমি দর্শনে আশ্চর্য হয়েছি যে বলে যে Origin.X = 23;সংকলকটির মতো কোডের প্রয়োজন মতো কোনও স্ট্রাক্ট ডিজাইন করা উচিত নয় Origin.X = new Point(23, Origin.Y);। পরেরটি আমার কাছে সত্যিই অদ্ভুত বলে মনে হচ্ছে।
সুপারক্যাট

@ সুপের্যাট এটিই প্রথম যখন আমি আপনার পয়েন্টটি ভাবছি, অনেক অর্থবোধ করে! এটিকে মোকাবেলার জন্য কী আপনার কাছে বিকল্প প্যাটার্ন / ডিজাইন ধারণা রয়েছে? যদি সি # ডিফল্টরূপে কোনও কাঠামোর জন্য ডিফল্ট কনস্ট্রাক্টর সরবরাহ না করানো সহজ হত (সেক্ষেত্রে আমাকে কঠোরভাবে উভয় Xএবং Yনির্দিষ্ট কনস্ট্রাক্টরকেই পাস করতে হবে )। এখন এটি পয়েন্টটি হারাবে যখন কেউ করতে পারে Point p = new Point()। আমি জানি কেন এটি স্ট্রাক্টের জন্য সত্যই প্রয়োজনীয়, তাই এটির পিছনে ভাবার কোনও কারণ নেই। তবে ঠিক Xকীভাবে একটির মতো সম্পত্তি আপডেট করার জন্য আপনার কাছে দুর্দান্ত ধারণা আছে ?
নওফাল

যে স্ট্রাক্টগুলি স্বতন্ত্র তবে সম্পর্কিত ভেরিয়েবলের সংকলনকে আবদ্ধ করে (যেমন কোনও বিন্দুর স্থানাঙ্ক), আমার পছন্দটি কেবল স্ট্রাক্টের সমস্ত সদস্যকে পাবলিক ফিল্ড হিসাবে প্রকাশ করা; কাঠামোগত সম্পত্তির একজন সদস্যকে সংশোধন করতে, কেবল এটি পড়ুন, সদস্যকে সংশোধন করুন এবং এটিকে আবার লিখুন। এটি অনেক ভাল হত যদি সি # একটি "সাধারণ সরল-পুরাতন-ডেটা-কাঠামো" ঘোষণা সরবরাহ করে যা স্বয়ংক্রিয়ভাবে এমন কোনও নির্মাতার সংজ্ঞা দেবে যার প্যারামিটারের তালিকাটি ক্ষেত্র তালিকার সাথে মিলেছে, তবে সি # এর জন্য দায়ী ব্যক্তিরা পারস্পরিক পরিবর্তনকে তুচ্ছ করে।
সুপারক্যাট

@ সুপের্যাট আমি এটি পেয়েছি স্ট্রাইক এবং ক্লাসগুলির অসামঞ্জস্যপূর্ণ আচরণ বিভ্রান্তিকর।
নওফাল

আইএমএইচওর অসহায় বিশ্বাস থেকে এই বিভ্রান্তির ফলস্বরূপ যে সমস্ত কিছু একটি শ্রেণি অবজেক্টের মতো আচরণ করা উচিত। হিপ অবজেক্টের রেফারেন্সের প্রত্যাশা করে এমন জিনিসগুলিতে মূল্য-ধরণের মানগুলি পাস করার উপায় থাকা দরকারী, তবে মান-ধরণের ভেরিয়েবলগুলি যে জিনিসগুলি থেকে প্রাপ্ত তা ধরে রাখতে কার্যকর হয় না Object। তারা না। প্রতিটি মান-ধরণের সংজ্ঞা আসলে দুটি ধরণের জিনিস সংজ্ঞায়িত করে: স্টোরেজ অবস্থানের ধরণ (ভেরিয়েবল, অ্যারে স্লট ইত্যাদির জন্য ব্যবহৃত হয়) এবং একটি হিপ অবজেক্ট টাইপ, যা কখনও কখনও "বাক্সযুক্ত" প্রকার হিসাবে ব্যবহৃত হয় (যখন মান-ধরণের মান হিসাবে ব্যবহৃত হয়) একটি রেফারেন্স-ধরণের স্থানে সংরক্ষিত থাকে)।
সুপারক্যাট

2

ক্লাস বনাম স্ট্রাক্টের পক্ষে মতামত নিয়ে বিতর্ক করা ছাড়াও আমি লক্ষ্যটি লক্ষ্য করি এবং সেই দৃষ্টিকোণ থেকে সমস্যার দিকে এগিয়ে যাই।

বলা হচ্ছে, যদি আপনার সম্পত্তিটির পেছনের কোডটি লেখার প্রয়োজন নেই এবং পদ্ধতিগুলি (যেমন আপনার উদাহরণ হিসাবে) সেট করার দরকার নেই, তবে কেবল Originসম্পত্তি হিসাবে শ্রেণীর ক্ষেত্র হিসাবে ঘোষণা করা কি সহজ হবে না ? আমার মনে করা উচিত এটি আপনাকে আপনার লক্ষ্য অর্জনের অনুমতি দেবে।

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

class MyClass
{
    public Point Origin;
}

MyClass c = new MyClass();
c.Origin.X = 23;   // No error.  Sets X just fine

0

সমস্যাটি হ'ল আপনি স্ট্যাকের উপরে অবস্থিত কোনও মানকে নির্দেশ করেছেন এবং মানটি আবার অরিগনাল সম্পত্তিতে ফিরে আসবে না যাতে সি # আপনাকে কোনও মানের ধরণের রেফারেন্স ফেরত দিতে দেয় না। আমি মনে করি আপনি উত্স সম্পত্তিটি মুছে ফেলে এর পরিবর্তে সর্বজনীনভাবে দায়েরকৃত পাবলিক ব্যবহার করতে পারেন, হ্যাঁ আমি জানি এটি কোনও দুর্দান্ত সমাধান নয়। অন্য সমাধানটি হ'ল পয়েন্টটি ব্যবহার না করা এবং এর পরিবর্তে কোনও অবজেক্ট হিসাবে নিজের পয়েন্ট টাইপ তৈরি করা।


যদি এটি Pointকোনও রেফারেন্স টাইপের সদস্য হয় তবে এটি স্ট্যাকের মধ্যে থাকবে না, এটি ধারণকৃত বস্তুর স্মৃতিতে স্তূপে থাকবে।
গ্রেগ বিচ

0

আমার ধারণা এখানে ধরা হ'ল আপনি বস্তুকে নিজেই বরাদ্দ না করে বিবৃতিতে অবজেক্টের সাব-ভ্যালু নির্ধারণের চেষ্টা করছেন। সম্পত্তির ধরণ পয়েন্ট হওয়ায় আপনাকে এই ক্ষেত্রে পুরো পয়েন্ট অবজেক্টটি নির্ধারণ করতে হবে।

Point newOrigin = new Point(10, 10);
Origin = newOrigin;

আশা করি আমি সেখানে বুঝেছি


2
গুরুত্বপূর্ণ পয়েন্টটি হ'ল পয়েন্টটি একটি স্ট্রাক্ট (ভ্যালুয়েটাইপ)। যদি এটি কোনও শ্রেণি (অবজেক্ট) হত তবে মূল কোডটি কাজ করত।
হ্যানস কে

@ হ্যানসকেস্টিং: যদি কোনও মিউটেবল Pointক্লাসের ধরণ হত তবে মূল কোডটি সম্পত্তি Xদ্বারা প্রত্যাবর্তিত অবজেক্টে ক্ষেত্র বা সম্পত্তি নির্ধারণ করতে পারে OriginOriginসম্পত্তি যুক্ত জিনিসটির উপর এটির কাঙ্ক্ষিত প্রভাব পড়বে বলে বিশ্বাস করার কোনও কারণ আমি দেখতে পাচ্ছি না । কিছু ফ্রেমওয়ার্ক ক্লাসে এমন বৈশিষ্ট্য রয়েছে যা তাদের রাজ্যটিকে নতুন পরিবর্তিত শ্রেণির উদাহরণগুলিতে অনুলিপি করে এবং সেগুলি ফেরত দেয়। এই জাতীয় নকশায় thing1.Origin = thing2.Origin;কোডটির সাথে অন্যের সাথে মিলের জন্য অবজেক্টের উত্সের অবস্থা নির্ধারণের সুবিধা দেওয়ার সুবিধা রয়েছে তবে এটি কোডের মতো সম্পর্কে সতর্ক করতে পারে না thing1.Origin.X += 4;
সুপারক্যাট

0

কেবল অনুসরণ হিসাবে সম্পত্তি "সেট সেট" সরান, এবং তারপরে সবকিছু যথারীতি কাজ করে।

আদিম ধরণের ক্ষেত্রে ইনস্ট্রেড ব্যবহার করুন; সেট; ... ব্যবহার করুন

using Microsoft.Xna.Framework;
using System;

namespace DL
{
    [Serializable()]
    public class CameraProperty
    {
        #region [READONLY PROPERTIES]
        public static readonly string CameraPropertyVersion = "v1.00";
        #endregion [READONLY PROPERTIES]


        /// <summary>
        /// CONSTRUCTOR
        /// </summary>
        public CameraProperty() {
            // INIT
            Scrolling               = 0f;
            CameraPos               = new Vector2(0f, 0f);
        }
        #region [PROPERTIES]   

        /// <summary>
        /// Scrolling
        /// </summary>
        public float Scrolling { get; set; }

        /// <summary>
        /// Position of the camera
        /// </summary>
        public Vector2 CameraPos;
        // instead of: public Vector2 CameraPos { get; set; }

        #endregion [PROPERTIES]

    }
}      

0

আমি মনে করি এখানে প্রচুর লোক বিভ্রান্ত হচ্ছে, এই বিশেষ সমস্যাটি বোঝার সাথে সম্পর্কিত যে মান ধরণের বৈশিষ্ট্যগুলি মান ধরণের একটি অনুলিপি (পদ্ধতি এবং সূচকগুলির মতো) প্রদান করে এবং মান ধরণের ক্ষেত্রগুলি সরাসরি অ্যাক্সেস করা হয় । নিম্নোক্ত কোডটি সম্পত্তির ব্যাকিং ফিল্ড সরাসরি সরাসরি অ্যাক্সেসের মাধ্যমে আপনি যা অর্জন করতে চাইছেন তা ঠিক করে দেয় (দ্রষ্টব্য: একটি ব্যাকিং ফিল্ডের সাথে তার ভারবস আকারে কোনও সম্পত্তি প্রকাশ করা একটি স্বয়ংক্রিয় সম্পত্তির সমতুল্য, তবে আমাদের সুবিধা অনুসারে সুবিধাটি রয়েছে যে আমাদের কোডে আমরা পারব সরাসরি সমর্থন ক্ষেত্র অ্যাক্সেস করুন):

class Program
{
    static void Main(string[] args)
    {
        var myClass = new MyClass();
        myClass.SetOrigin();
        Debug.Assert(myClass.Origin.X == 10); //succeeds
    }
}

class MyClass
{
    private Point _origin;
    public Point Origin
    { 
        get => _origin; 
        set => _origin = value; 
    }

    public void SetOrigin()
    {
        _origin.X = 10; //this works
        //Origin.X = 10; // fails with CS1612;
    }
}

আপনি যে ত্রুটিটি পাচ্ছেন সেটি হ'ল বোঝার একটি পরোক্ষ পরিণতি যা কোনও সম্পত্তি কোনও মানের ধরণের অনুলিপি দেয়। যদি আপনাকে কোনও মান ধরণের একটি অনুলিপি ফেরত দেওয়া হয় এবং আপনি এটি স্থানীয় ভেরিয়েবলের কাছে অর্পণ করেন না তবে সেই অনুলিপিতে আপনার করা পরিবর্তনগুলি কখনই পড়তে পারে না এবং তাই সংকলক এটিকে ত্রুটি হিসাবে উত্থাপন করে এটি উদ্দেশ্যমূলক হতে পারে না। যদি আমরা কোনও স্থানীয় ভেরিয়েবলের অনুলিপিটি অর্পণ করি তবে আমরা এক্সের মানটি পরিবর্তন করতে পারি তবে এটি কেবল স্থানীয় অনুলিপিতেই পরিবর্তিত হবে যা সংকলনের সময় ত্রুটিটি স্থির করে, তবে মূল বৈশিষ্ট্যটি সংশোধন করার পছন্দসই প্রভাব ফেলবে না। সংকলন ত্রুটি চলে যাওয়ার পরে নিম্নলিখিত কোডটি এটি চিত্রিত করে, তবে ডিবাগের দৃser়তা ব্যর্থ হবে:

class Program
{
    static void Main(string[] args)
    {
        var myClass = new MyClass();
        myClass.SetOrigin();
        Debug.Assert(myClass.Origin.X == 10); //throws error
    }
}

class MyClass
{
    private Point _origin;
    public Point Origin
    { 
        get => _origin; 
        set => _origin = value; 
    }

    public void SetOrigin()
    {
        var origin = Origin;
        origin.X = 10; //this is only changing the value of the local copy
    }
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.