সি # স্ট্রিং রেফারেন্স টাইপ?


163

আমি জানি যে সি # তে "স্ট্রিং" একটি রেফারেন্স টাইপ। এটি এমএসডিএন-এ রয়েছে। তবে এই কোডটি তখনকার মতো কাজ করে না:

class Test
{
    public static void Main()
    {
        string test = "before passing";
        Console.WriteLine(test);
        TestI(test);
        Console.WriteLine(test);
    }

    public static void TestI(string test)
    {
        test = "after passing";
    }
}

আউটপুটটি "পাস করার আগে" "পাস করার আগে" হওয়া উচিত যেহেতু আমি প্যারামিটার হিসাবে স্ট্রিংটি পাস করছি এবং এটি একটি রেফারেন্স টাইপ, দ্বিতীয় আউটপুট স্টেটমেন্টটি সনাক্ত করতে হবে যে টেস্টি পদ্ধতিতে পাঠ্য পরিবর্তন হয়েছে। যাইহোক, আমি "পাস করার আগে" "পাস করার আগে" পেয়েছি বলে মনে হচ্ছে এটি রেফ দ্বারা নয় মান দ্বারা পাস হয়েছে। আমি বুঝতে পারি যে স্ট্রিংগুলি অপরিবর্তনীয়, তবে আমি কী দেখছি তা এখানে কী চলছে তা ব্যাখ্যা করবে। আমি কী মিস করছি? ধন্যবাদ।


নীচে জন দ্বারা নিবন্ধটি নিবন্ধটি দেখুন। আপনি যে আচরণটি উল্লেখ করেছেন সেটি সি ++ পয়েন্টার দ্বারাও পুনরুত্পাদন করা যেতে পারে।
সেশ

এমএসডিএন- তেও খুব সুন্দর ব্যাখ্যা ।
দিমি_পেল

উত্তর:


211

স্ট্রিংয়ের উল্লেখটি মান দ্বারা পাস করা হয়। মান দ্বারা একটি রেফারেন্স পাস এবং রেফারেন্স দ্বারা একটি বস্তু পাস করার মধ্যে একটি বড় পার্থক্য আছে। দুর্ভাগ্য যে "রেফারেন্স" শব্দটি উভয় ক্ষেত্রেই ব্যবহৃত হয়।

আপনি যদি না স্ট্রিং রেফারেন্স পাস দ্বারা রেফারেন্স, এটা হিসাবে আপনি আশা কাজ করবে:

using System;

class Test
{
    public static void Main()
    {
        string test = "before passing";
        Console.WriteLine(test);
        TestI(ref test);
        Console.WriteLine(test);
    }

    public static void TestI(ref string test)
    {
        test = "after passing";
    }
}

এখন আপনাকে কোনও রেফারেন্স উল্লেখ করা বস্তুটিতে পরিবর্তন করার এবং একটি পৃথক অবজেক্টের উল্লেখ করার জন্য একটি ভেরিয়েবলের (যেমন একটি প্যারামিটার) পরিবর্তন করার মধ্যে পার্থক্য করতে হবে। স্ট্রিংগুলি পরিবর্তনযোগ্য না হওয়ায় আমরা স্ট্রিংয়ে পরিবর্তন আনতে পারি না তবে এর StringBuilderপরিবর্তে আমরা এটি প্রদর্শন করতে পারি :

using System;
using System.Text;

class Test
{
    public static void Main()
    {
        StringBuilder test = new StringBuilder();
        Console.WriteLine(test);
        TestI(test);
        Console.WriteLine(test);
    }

    public static void TestI(StringBuilder test)
    {
        // Note that we're not changing the value
        // of the "test" parameter - we're changing
        // the data in the object it's referring to
        test.Append("changing");
    }
}

আরও তথ্যের জন্য প্যারামিটার পাস করার বিষয়ে আমার নিবন্ধটি দেখুন ।


2
সম্মত হন, কেবল পরিষ্কার করতে চান যে রেফ সংশোধক ব্যবহার করে রেফারেন্স প্রকারের জন্যও কাজ করে অর্থাৎ উভয়ই পৃথক পৃথক ধারণা।
উদাহরণস্বরূপ

2
@ জোন স্কিটি আপনার নিবন্ধে সিডেনোট পছন্দ করেছিল। referencedআপনার উত্তর হিসাবে আপনার এটি হওয়া উচিত
নীতিশ ইনপারসুট অফহ্যাপিনেস

36

যদি আমাদের প্রশ্নের উত্তর দিতে হয়: স্ট্রিং একটি রেফারেন্স টাইপ এবং এটি একটি রেফারেন্স হিসাবে আচরণ করে। আমরা একটি পরামিতি পাস করি যা একটি রেফারেন্স ধারণ করে, আসল স্ট্রিং নয়। সমস্যাটি ফাংশনে রয়েছে:

public static void TestI(string test)
{
    test = "after passing";
}

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

refফাংশন ঘোষণাপত্র এবং অনুরোধ কার্যক্রমে রাখার প্রস্তাবিত সমাধানগুলি কারণ আমরা testভেরিয়েবলের মানটি পাস করব না তবে এটির জন্য কেবল একটি রেফারেন্সই পাস করব। সুতরাং ফাংশনের ভিতরে যে কোনও পরিবর্তন আসল পরিবর্তনশীলকে প্রতিফলিত করবে।

আমি শেষে পুনরাবৃত্তি করতে চাই: স্ট্রিং একটি রেফারেন্স টাইপ তবে এর অপরিবর্তনীয় রেখা test = "after passing";আসলে একটি নতুন অবজেক্ট তৈরি করে এবং আমাদের ভ্যারিয়েবলের অনুলিপিটি testনতুন স্ট্রিংয়ের দিকে নির্দেশিত হয়।


25

অন্যরা যেমন বলেছে, String.NET টাইপটি অপরিবর্তনীয় এবং এর রেফারেন্সটি মান দিয়ে যায়।

মূল কোডে, এই লাইনটি কার্যকর হওয়ার সাথে সাথে:

test = "after passing";

তারপরে testআর আসল অবজেক্টের উল্লেখ নেই । আমরা একটি নতুন String অবজেক্ট তৈরি করেছি এবং testপরিচালিত হিপগুলিতে সেই বিষয়টিকে রেফারেন্সের জন্য বরাদ্দ করেছি ।

আমি মনে করি যে এখানে অনেক লোক এখানে বিভ্রান্ত হয়ে পড়েছে যেহেতু তাদের মনে করিয়ে দেওয়ার মতো কোনও দৃশ্যমান রচয়িতা নেই। এই ক্ষেত্রে, এটি পর্দার পিছনে ঘটছে যেহেতু Stringটাইপটি কীভাবে এটি নির্মিত হয় তাতে ভাষা সমর্থন রয়েছে।

সুতরাং, পরিবর্তনের জন্য পদ্ধতির testআওতার বাইরে দৃশ্যমান নয় TestI(string)- আমরা মান অনুসারে রেফারেন্সটি পেরিয়েছি এবং এখন সেই মানটি পরিবর্তিত হয়েছে! তবে যদি Stringরেফারেন্সটি রেফারেন্স দিয়ে পাস করা হয়, তবে যখন রেফারেন্স পরিবর্তন হবে আমরা এটি TestI(string)পদ্ধতির আওতার বাইরে দেখতে পাব ।

এক্ষেত্রে হয় রেফ বা আউট কীওয়ার্ডের প্রয়োজন। আমি মনে করি outকীওয়ার্ডটি এই নির্দিষ্ট পরিস্থিতির জন্য কিছুটা ভাল উপযুক্ত হতে পারে।

class Program
{
    static void Main(string[] args)
    {
        string test = "before passing";
        Console.WriteLine(test);
        TestI(out test);
        Console.WriteLine(test);
        Console.ReadLine();
    }

    public static void TestI(out string test)
    {
        test = "after passing";
    }
}

রেফ = বাহ্যিক ক্রিয়াকলাপ, আউট = ফাংশনের অভ্যন্তর বা অন্য কথায় শুরু; রেফ দ্বি-উপায়, আউট কেবলমাত্র। সুতরাং অবশ্যই রেফ ব্যবহার করা উচিত।
পল জহরা

@ পলজাহাহার: outকোডটি সংকলনের জন্য পদ্ধতির মধ্যে নির্ধারিত হওয়া দরকার। refএর মতো কোনও প্রয়োজন নেই। এছাড়াও outপ্যারামিটারগুলি পদ্ধতির বাইরে সূচনা করা হয় - এই উত্তরের কোডটি একটি কাউন্টারিক নমুনা।
ডেরেক ডাব্লু

স্পষ্ট করা উচিত - outপ্যারামিটারগুলি পদ্ধতির বাইরে সূচনা করা যেতে পারে তবে এটি করার দরকার নেই। এই ক্ষেত্রে, আমরা .NET টাইপের outপ্রকৃতি সম্পর্কে একটি পয়েন্ট প্রদর্শন করতে প্যারামিটারটি শুরু করতে চাই string
ডেরেক ডাব্লু

9

প্রকৃতপক্ষে এটি যে কোনও বস্তুর ক্ষেত্রে একই ছিল যেমন রেফারেন্স টাইপ হওয়া এবং রেফারেন্সের মাধ্যমে পাস করা সি # তে 2 ভিন্ন জিনিস different

এটি কাজ করবে, তবে প্রকারটি নির্বিশেষে এটি প্রযোজ্য:

public static void TestI(ref string test)

স্ট্রিং সম্পর্কে একটি রেফারেন্স টাইপ, এটি একটি বিশেষ। এটি অপরিবর্তনীয় হিসাবে ডিজাইন করা হয়েছে, সুতরাং এর সমস্ত পদ্ধতিতে উদাহরণটি সংশোধন করবে না (তারা একটি নতুন ফেরত দেয়)। পারফরম্যান্সের জন্য এতে কিছু অতিরিক্ত জিনিসও রয়েছে।


7

মান-প্রকার, পাসিং-বাই-ভ্যালু, রেফারেন্স-প্রকার এবং পাস-বাই-রেফারেন্সের মধ্যে পার্থক্য সম্পর্কে ভাবার জন্য এখানে একটি ভাল উপায়'s

একটি পরিবর্তনশীল একটি ধারক হয়।

একটি মান-ধরণের ভেরিয়েবলের একটি উদাহরণ রয়েছে। একটি রেফারেন্স-টাইপ ভেরিয়েবল অন্য কোথাও সঞ্চিত উদাহরণের জন্য একটি পয়েন্টার ধারণ করে।

একটি মান-ধরণের ভেরিয়েবল সংশোধন করে এটি থাকা দৃষ্টান্তটিকে পরিবর্তন করে। একটি রেফারেন্স-টাইপ ভেরিয়েবল সংশোধন করে যে দৃষ্টান্তটি নির্দেশ করে তা রূপান্তরিত করে।

পৃথক পৃথক রেফারেন্স-ধরণের ভেরিয়েবল একই উদাহরণে নির্দেশ করতে পারে। অতএব, একই দৃষ্টিকোণটি এটির নির্দেশ করে এমন কোনও ভেরিয়েবলের মাধ্যমে পরিবর্তিত হতে পারে।

একটি পাস-বাই-মান আর্গুমেন্ট সামগ্রীটির একটি নতুন অনুলিপি সহ একটি নতুন ধারক। একটি পাস-বাই-রেফারেন্স যুক্তি হ'ল তার আসল সামগ্রী সহ মূল ধারক।

যখন একটি মান-ধরণের যুক্তি-দ্বারা-মানটি পাস করা হয়: আর্গুমেন্টের বিষয়বস্তুটি পুনরায় স্বাক্ষর করার সুযোগের বাইরে কোনও প্রভাব থাকে না, কারণ ধারকটি অনন্য। যুক্তি সংশোধন করার সুযোগের বাইরে কোনও প্রভাব নেই, কারণ উদাহরণটি একটি স্বতন্ত্র অনুলিপি।

যখন একটি রেফারেন্স-ধরণের আর্গুমেন্টটি বাই-মান দিয়ে যায়: আর্গুমেন্টের বিষয়বস্তু পুনরায় স্বাক্ষর করার সুযোগের বাইরে কোনও প্রভাব থাকে না, কারণ ধারকটি অনন্য। আর্গুমেন্টের বিষয়বস্তু সংশোধন করা বাহ্যিক সুযোগকে প্রভাবিত করে, কারণ অনুলিপি পয়েন্টারটি একটি ভাগ করা উদাহরণকে নির্দেশ করে।

কোনও যুক্তি যখন রেফারেন্স দ্বারা প্রেরণ করা হয়: তর্কটির বিষয়বস্তু পুনরায় স্থাপন করা বাহ্যিক সুযোগকে প্রভাবিত করে, কারণ ধারকটি ভাগ করা আছে। যুক্তিটির বিষয়বস্তু সংশোধন করা বাহ্যিক সুযোগকে প্রভাবিত করে, কারণ সামগ্রীটি ভাগ করা আছে।

উপসংহারে:

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


6

" একটি ছবি হাজার শব্দের জন্য মূল্যবান "।

আমি এখানে একটি সাধারণ উদাহরণ আছে, এটি আপনার ক্ষেত্রে অনুরূপ।

string s1 = "abc";
string s2 = s1;
s1 = "def";
Console.WriteLine(s2);
// Output: abc

এটাই হযেছিল:

এখানে চিত্র বর্ণনা লিখুন

  • লাইন 1 এবং 2: s1এবং s2একই "abc"স্ট্রিং অবজেক্টে ভেরিয়েবলের রেফারেন্স ।
  • লাইন 3: স্ট্রিংগুলি অপরিবর্তনীয় , তাই "abc"স্ট্রিং অবজেক্ট নিজেকে (টু "def") পরিবর্তন করে না , "def"পরিবর্তে একটি নতুন স্ট্রিং অবজেক্ট তৈরি করা হয় এবং তারপরে s1এটি উল্লেখ করা হয়।
  • লাইন 4: s2এখনও "abc"স্ট্রিং অবজেক্টের উল্লেখ , যাতে আউটপুট।

5

উপরের উত্তরগুলি সহায়ক, আমি কেবল একটি উদাহরণ যুক্ত করতে চাই যা আমি মনে করি যে স্পষ্টভাবে প্রদর্শন করছি যখন আমরা রেফার কীওয়ার্ড ছাড়াই প্যারামিটারটি পাস করি তখনও কী ঘটে that প্যারামিটারটি একটি রেফারেন্স টাইপ হলেও:

MyClass c = new MyClass(); c.MyProperty = "foo";

CNull(c); // only a copy of the reference is sent 
Console.WriteLine(c.MyProperty); // still foo, we only made the copy null
CPropertyChange(c); 
Console.WriteLine(c.MyProperty); // bar


private void CNull(MyClass c2)
        {          
            c2 = null;
        }
private void CPropertyChange(MyClass c2) 
        {
            c2.MyProperty = "bar"; // c2 is a copy, but it refers to the same object that c does (on heap) and modified property would appear on c.MyProperty as well.
        }

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

3

কৌতূহলী মন এবং কথোপকথনটি সম্পূর্ণ করার জন্য: হ্যাঁ, স্ট্রিং একটি রেফারেন্স টাইপ :

unsafe
{
     string a = "Test";
     string b = a;
     fixed (char* p = a)
     {
          p[0] = 'B';
     }
     Console.WriteLine(a); // output: "Best"
     Console.WriteLine(b); // output: "Best"
}

তবে মনে রাখবেন যে এই পরিবর্তনটি কেবল একটি অনিরাপদ ব্লকে কাজ করে! কারণ স্ট্রিংগুলি অপরিবর্তনীয় (এমএসডিএন থেকে):

বস্তুটি তৈরি হওয়ার পরে একটি স্ট্রিং অবজেক্টের বিষয়বস্তু পরিবর্তন করা যায় না, যদিও সিনট্যাক্সটি এটিকে উপস্থিত করে তোলে যেন আপনি এটি করতে পারেন। উদাহরণস্বরূপ, আপনি এই কোডটি লেখার সময়, সংকলকটি আসলে অক্ষরের নতুন ক্রম ধরে রাখতে একটি নতুন স্ট্রিং অবজেক্ট তৈরি করে এবং সেই নতুন অবজেক্টটি বি কে নির্ধারিত করা হয়। স্ট্রিং "এইচ" এর পরে আবর্জনা সংগ্রহের জন্য উপযুক্ত।

string b = "h";  
b += "ello";  

এবং মনে রাখবেন যে:

যদিও স্ট্রিং একটি রেফারেন্স টাইপ, সমতা অপারেটরগুলি ( ==এবং !=) স্ট্রিং অবজেক্টের মানগুলির তুলনা করতে সংজ্ঞায়িত করা হয়, রেফারেন্স নয়।


0

আমি বিশ্বাস করি যে আপনার কোডটি নীচের সাথে সাদৃশ্যপূর্ণ এবং আপনার এখানে মানটি একই কারণে পরিবর্তিত হবে বলে আশা করা উচিত নয়:

 public static void Main()
 {
     StringWrapper testVariable = new StringWrapper("before passing");
     Console.WriteLine(testVariable);
     TestI(testVariable);
     Console.WriteLine(testVariable);
 }

 public static void TestI(StringWrapper testParameter)
 {
     testParameter = new StringWrapper("after passing");

     // this will change the object that testParameter is pointing/referring
     // to but it doesn't change testVariable unless you use a reference
     // parameter as indicated in other answers
 }

-1

চেষ্টা করুন:


public static void TestI(ref string test)
    {
        test = "after passing";
    }

3
আপনার উত্তরে কেবল কোডের চেয়ে বেশি থাকা উচিত। এটি কেন কাজ করে তাও এর একটি ব্যাখ্যা থাকতে হবে।
চার্লস
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.