что-нибудь вроде указателей в С#?

Я новичок в С# (и кодировании в целом) и не могу найти ничего эквивалентного указателю.
Когда я искал в Google, я получил что-то вроде безопасного/небезопасного, но это не то, что мне нужно.

Как и в C++, если бы у вас был указатель и он указывал на какое-то значение, изменение указателя вызывало бы изменение исходной переменной. Есть ли что-нибудь подобное в c#?
пример-

static class universal
{
   public static int a = 10;
}

class class_a
{
   public void change1()
   {
      universal.a--;
   }
}

class class_b
{
   public void change2()
   {
      some_keyword temp = universal.a; //change in temp gives change in a
      temp-= 5; //the purpose of temp is to NOT have to write universal.a each time
   }
}

...

static void Main(string[] args)
{
   class_b B = new class_b();
   class_a A = new class_a();
   A.change1();
   Console.WriteLine(universal.a);//it will print 9

   B.change2();
   Console.WriteLine(universal.a);//it will print 4
   Console.ReadKey();
}

Редактировать - спасибо @Sweeper, я получил ответ, который мне пришлось использовать ref int temp = ref universal.a;


person Wired Differently    schedule 04.12.2020    source источник
comment
Типы значений и Типы ссылок   -  person TheGeneral    schedule 04.12.2020
comment
Как и в C++, если бы у вас был указатель и он указывал на какое-то значение, изменение указателя вызывало бы изменение исходной переменной. Есть ли что-то подобное в c#? - Посмотрите еще раз!   -  person TaW    schedule 04.12.2020
comment
Pass By Reference решит вашу проблему.   -  person AndroidLearner    schedule 04.12.2020


Ответы (5)


Если вам не нужен небезопасный код, я могу придумать два варианта.

Объект-оболочка

Вы можете создать такой класс, который обертывает int:

public class IntWrapper {
    public int Value { get; set; }
}

Затем измените тип a на этот класс:

static class Universal
{
   public static IntWrapper a = new IntWrapper { Value = 10 };
}

class class_a
{
   public void change1()
   {
      universal.a.Value--;
   }
}

class class_b
{
   public void change2()
   {
      Universal temp = universal.a; //change in temp gives change in a
      temp.Value -= 5;
   }
}

Это работает, потому что классы являются ссылочными типами, а a содержит ссылку (аналогично указателю) на объект IntWrapper. = копирует ссылку на temp без создания нового объекта. И temp, и a относятся к одному и тому же объекту.

ref locals

Это более простой способ, но он подходит только для локальных переменных. Например, вы не можете использовать это для поля.

public void change2()
{
    ref int temp = ref universal.a; //change in temp gives change in a
    temp -= 5;
}
person Sweeper    schedule 04.12.2020
comment
ну, на самом деле я не хочу писать universal.a каждый раз при выполнении функции change2(), вместо этого просто замените его одним именем переменной temp, необходимость писать temp.a на самом деле лишает цели ¯_(ツ)_/ ¯ - person Wired Differently; 04.12.2020
comment
@WiredDifferently Упс. Я скопировал ваш код и случайно оставил там .a. Вам не нужно .a, если вы используете ref местных жителей. Вам нужен .Value, но не .a, если вы используете объект-оболочку. - person Sweeper; 04.12.2020
comment
он по-прежнему выдает ошибку: невозможно инициализировать переменную по ссылке со значением - person Wired Differently; 04.12.2020
comment
@WiredDifferently Не забудьте также слово ref перед universal.a. Я забыл это, и теперь я добавил это. - person Sweeper; 04.12.2020
comment
Большое спасибо, я получил решение, но просто из чистого любопытства можно ли использовать эту ссылку для всех типов данных? например, я работаю над некоторым кодом селена, и мне нужно использовать IWebDriver вместо int (не нужно отвечать на это, если вы не хотите, чтобы это не было точно связано с Q, который я опубликовал) - person Wired Differently; 04.12.2020
comment
@WiredDifferently Прочтите документы, на которые я ссылаюсь. Это гораздо надежнее моей памяти :) IIRC, да. Но обратите внимание, что IWebDriver сам по себе является ссылочным типом, поэтому, если вы не собираетесь менять ссылку с помощью =, вам вообще не нужна ref. например когда вы хотите изменить его Url, вы не изменяете саму переменную, а скорее мутируете объект. В этом случае вам не нужно ref. Если вы запутались, прочитайте ссылки, которые The General предоставил в комментариях к вашему вопросу. - person Sweeper; 04.12.2020

В C# есть references, которые очень похожи на указатели. Если a и b являются ссылками на один и тот же объект, изменение в a также будет видно в b.

Например, в:

class X {
    public int val;
}

void Main()
{
    var a = new X();
    var b = a;
    a.val = 6;
    Console.WriteLine(b.val);
}

6 будет написано.

Если вы измените объявление X с class на struct, то a и b больше не будут ссылками, а 0 будет записано.

person JoelFan    schedule 04.12.2020
comment
на самом деле вся моя точка зрения состоит в том, чтобы заменить необходимость писать universal.a каждый раз, а вместо этого просто написать temp (одна переменная) и использовать этот temp через функцию change2 - person Wired Differently; 04.12.2020

В С# вместо указателей используется Pass By Reference. Вот исправленный код

static class universal
{
   public static int a = 10;
}

class class_a
{
   public void change1()
   {
      universal.a--;
   }
}

class class_b
{
   public void change2(ref int val)//use ref keyword for reference
   {
       int temp = val; //change in temp gives change in a
       val -= 5;
   }
}

static void Main(string[] args)
{
   class_b B = new class_b();
   class_a A = new class_a();
   A.change1();
   Console.WriteLine(universal.a);//it will print 9

   B.change2(ref universal.a); //pass value by reference using ref keyword
   Console.WriteLine(universal.a);//it will print 4
   Console.ReadKey();
}
person Techie Boy    schedule 04.12.2020
comment
есть ли способ, с помощью которого нам не нужно использовать ref в параметре функции, а вместо этого напрямую назначать temp внутри функции? вся моя цель состоит в том, чтобы НЕ писать universal.a каждый раз? и просто замените его на temp через функцию change2(), но изменение temp по-прежнему вызывает изменение в universal.a - person Wired Differently; 04.12.2020
comment
вы найдете свой ответ здесь: stackoverflow.com/questions/2333574/ - person Techie Boy; 04.12.2020
comment
Используйте pass by reference, вы уже объявили, что член является статическим, почему бы вам не использовать его напрямую? - person Agent Smith; 04.12.2020

В некоторых случаях (когда очень нужна оптимизация) можно использовать почти Си-подобные указатели. Вы можете сделать это, только явно указав, что знаете о риске, поместив свой код в область unsafe:

unsafe
{
    int number = 777;
    int* ptr = &number;

    Console.WriteLine($"Data that pointer points to is: {number} ");
    Console.WriteLine($"Address that pointer holds: {(int)ptr}");
}

Небезопасный контекст позволяет вам напрямую использовать указатели. Обратите внимание, что по умолчанию эта опция отключена в вашем проекте. Чтобы проверить это, вам нужно щелкнуть правой кнопкой мыши проект›Свойства›Сборка — Разрешить небезопасный код.

person dimitar.d    schedule 04.12.2020

Как это?

using System;

namespace Demo
{
    class Program
    {
        static void Main()
        {
            var test = new class_a();
            test.change1();
            Console.WriteLine(universal.a); // Prints 18
        }
    }

    static class universal
    {
        public static int a = 10;
    }

    class class_a
    {
        public void change1()
        {
            ref int x = ref universal.a;

            ++x;
            ++x;
            ++x;
            x += 5;
        }
    }
}

[РЕДАКТИРОВАТЬ: я заметил, что это то же самое, что и последняя часть ответа Sweeper, но я оставлю это здесь, поскольку оно сосредоточено только на этом решении.]

person Matthew Watson    schedule 04.12.2020