Почему я не могу передать более общий объект в параметр out? [Дубликат]

Конечно, есть много таких подходов, как синхронный запрос, обещание, но из моего опыта я думаю, что вы должны использовать подход обратного вызова. Естественно, что асинхронное поведение Javascript. Итак, ваш фрагмент кода можно переписать немного иначе:

function foo() {
    var result;

    $.ajax({
        url: '...',
        success: function(response) {
            myCallback(response);
        }
    });

    return result;
}

function myCallback(response) {
    // Does something.
}
114
задан BartoszKP 18 October 2014 в 17:55
поделиться

10 ответов

=============

UPDATE: я использовал этот ответ в качестве основы для этой записи в блоге:

Почему ref и параметры не позволяют изменять тип?

См. страницу блога для получения дополнительных комментариев по этой проблеме. Спасибо за отличный вопрос.

=============

Предположим, что у вас есть классы Animal, Mammal, Reptile, Giraffe, Turtle и Tiger, с очевидными отношениями подкласса.

Теперь предположим, что у вас есть метод void M(ref Mammal m). M может читать и записывать m.


Можете ли вы передать переменную типа Animal на M?

№. Эта переменная может содержать Turtle, но M предполагает, что она содержит только млекопитающих. Turtle не является Mammal.

Заключение 1: параметры ref не могут быть сделаны «большими». (Есть больше животных, чем млекопитающих, поэтому переменная становится «больше», потому что она может содержать больше вещей.)


Можете ли вы передать переменную типа Giraffe на M?

№. M может записать на m, а M может захотеть записать Tiger в m. Теперь вы поместили Tiger в переменную, которая фактически имеет тип Giraffe.

Вывод 2: ref параметры не могут быть сделаны «меньше».


Теперь рассмотрим N(out Mammal n).

Можете ли вы передать переменную типа Giraffe на N?

№. N может писать в n, а N может захотеть записать Tiger.

Заключение 3: out параметры не могут быть сделаны «меньше».


Можете ли вы передать переменную типа Animal на N?

Хмм.

Ну, почему бы и нет? N не может читать из n, он может писать только ему, не так ли? Вы записываете Tiger в переменную типа Animal, и все вы настроены, верно?

Неверно. Правило не "N может писать только n".

Правила кратко:

1) N должен записать в n, прежде чем N вернется нормально. (Если N выбрасывает, все ставки отключены.)

2) N должен что-то записать в n, прежде чем он что-то прочитает из n.

разрешает эту последовательность событий:

  • Объявить поле x типа Animal.
  • Пропустить x как параметр out до N.
  • N записывает Tiger в n, который является псевдонимом для x.
  • В другом потоке кто-то пишет Turtle в x.
  • N пытается прочитать содержимое n и обнаруживает Turtle в том, что, по его мнению, является переменной типа Mammal.

Очевидно, мы хотим сделать это незаконным.

Заключение 4: out параметры не могут быть сделаны «большими».


Окончательное заключение : Ни параметры ref, ни out могут варьироваться в зависимости от их типа. Иначе говоря, нужно разбить безопасную безопасность типа.

Если эти вопросы в теории базового типа вас интересуют, подумайте о том, чтобы прочитать мою серию о том, как ковариация и контравариантность работают в C # 4.0 .

156
ответ дан Jeroen Vannevel 28 August 2018 в 08:17
поделиться

Вы боретесь с классической проблемой ковариации (и контравариантности) ООП, см. wikipedia : многое, поскольку этот факт может игнорировать интуитивные ожидания, математически невозможно разрешить замену производных классов вместо базовые для изменяемых (назначаемых) аргументов (а также контейнеров, чьи позиции можно присваивать по той же причине), все еще соблюдая принцип Лискова . Почему это так набросано в существующих ответах и ​​более подробно изучается в этих статьях и ссылках на них.

Языки ООП, которые, как представляется, делают это, оставаясь традиционно статически типичными, «обманывают» (вставляя скрытую динамику проверять тип, или требовать проверки всех источников во время компиляции); фундаментальный выбор: либо отказаться от этой ковариации и принять недоумение практикующих (как это делает C # здесь), либо перейти к подходу с динамической типизацией (как к самому первому языку ООП, Smalltalk), или перейти к неизменяемому (однострочному) задание), как и функциональные языки (по неизменности, вы можете поддерживать ковариацию, а также избегать других связанных головоломок, таких как тот факт, что вы не можете иметь прямоугольный подкласс Rectangle в мире изменяемых данных).

9
ответ дан Alex Martelli 28 August 2018 в 08:17
поделиться

В то время как другие ответы кратко объясняли причины такого поведения, я думаю, стоит упомянуть, что если вам действительно нужно что-то сделать такого рода, вы можете выполнить аналогичную функциональность, сделав Foo2 в общий метод, например:

class A {}

class B : A {}

class C
{
    C()
    {
        var b = new B();
        Foo(b);
        Foo2(ref b); // <= no compile error!
    }

    void Foo(A a) {}

    void Foo2<AType> (ref AType a) where AType: A {}  
}
1
ответ дан BrendanLoBuglio 28 August 2018 в 08:17
поделиться

Поскольку предоставление Foo2 a ref B приведет к некорректному объекту, потому что Foo2 знает, как заполнить A часть B.

2
ответ дан CannibalSmith 28 August 2018 в 08:17
поделиться

Разве это не то, что компилятор говорит вам, что вы хотите, чтобы я явно бросил объект, чтобы он мог быть уверен, что вы знаете, каковы ваши намерения?

Foo2(ref (A)b)
0
ответ дан dlamblin 28 August 2018 в 08:17
поделиться

Рассмотрим:

class C : A {}
class B : A {}

void Foo2(ref A a) { a = new C(); } 

B b = null;
Foo2(ref b);

Это нарушит безопасность типа

4
ответ дан Henk Holterman 28 August 2018 в 08:17
поделиться

Если вы используете практические примеры для ваших типов, вы увидите следующее:

SqlConnection connection = new SqlConnection();
Foo(ref connection);

И теперь у вас есть ваша функция, которая принимает предка ( ie Object):

void Foo2(ref Object connection) { }

Что может быть с этим связано?

void Foo2(ref Object connection)
{
   connection = new Bitmap();
}

Вам просто удалось назначить Bitmap на ваш SqlConnection.

Это нехорошо.


Повторите попытку с другими:

SqlConnection conn = new SqlConnection();
Foo2(ref conn);

void Foo2(ref DbConnection connection)
{
    conn = new OracleConnection();
}

Вы наполнили OracleConnection поверх вашего SqlConnection.

0
ответ дан Ian Boyd 28 August 2018 в 08:17
поделиться

Поскольку в обоих случаях вы должны иметь возможность присвоить значение параметру ref / out.

Если вы попытаетесь передать b в метод Foo2 в качестве ссылки, а в Foo2 вы попытаетесь определить a = new A (), это будет недействительным. По той же причине, которую вы не можете написать:

B b = new A();
28
ответ дан maciejkow 28 August 2018 в 08:17
поделиться

Имеет смысл с точки зрения безопасности, но я предпочел бы, чтобы компилятор выдал предупреждение вместо ошибки, поскольку существует законное использование полиморфных объектов, переданных по ссылке. например,

class Derp : interfaceX
{
   int somevalue=0; //specified that this class contains somevalue by interfaceX
   public Derp(int val)
    {
    somevalue = val;
    }

}


void Foo(ref object obj){
    int result = (interfaceX)obj.somevalue;
    //do stuff to result variable... in my case data access
    obj = Activator.CreateInstance(obj.GetType(), result);
}

main()
{
   Derp x = new Derp();
   Foo(ref Derp);
}

Это не будет компилироваться, но будет ли оно работать?

0
ответ дан Oofpez 28 August 2018 в 08:17
поделиться

В моем случае моя функция приняла объект, и я не мог отправить ничего, поэтому я просто сделал

object bla = myVar;
Foo(ref bla);

И это работает

My Foo находится в VB.NET и он проверяет тип внутри и делает много логики

Прошу прощения, если мой ответ повторяется, но другие слишком длинны

0
ответ дан Shereef Marzouk 28 August 2018 в 08:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: