Почему законно передавать «Я» ByRef в VB.NET? - PullRequest
4 голосов
/ 05 октября 2010

Я был потрясен минуту назад, когда обнаружил, что следующее является законным (эквивалент C # определенно нет):

Class Assigner
    ''// Ignore this for now.
    Public Field As Integer

    ''// This part is not so weird... take another instance ByRef,
    ''// assign it to a different instance -- stupid but whatever. '
    Sub Assign(ByRef x As Assigner, ByVal y As Assigner)
        x = y
    End Sub

    ''// But... what's this?!?
    Sub AssignNew()
        ''// Passing "Me" ByRef???
        Assign(Me, New Assigner)
    End Sub

    ''// This is just for testing.
    Function GetField() As Integer
        Return Me.Field
    End Function
End Class

Но что такое даже незнакомец Для меня так же странно то, что он не делает то, что я ожидаю:

Dim a As New Assigner With {.Field = 10}

a.AssignNew()

Console.WriteLine(a.GetField())

Выше приведено «10», а не «0», как я думал (хотя, естественно, само это ожидание было наполнено неким ужасом). Так что кажется, что вы можете передать Me ByRef, но компилятор каким-то образом переопределил поведение (?), Чтобы оно было , как если бы вы передали Me ByVal.

  1. Почему допустимо передавать Me ByRef? (есть какое-то объяснение обратной совместимости?)
  2. Правильно ли я сказал, что компилятор отменяет поведение при выполнении этого? Если нет, то что мне не хватает?

Ответы [ 4 ]

5 голосов
/ 05 октября 2010

Это поведение на самом деле следует непосредственно из спецификации Visual Basic.

11.4.3 Выражения экземпляров

Выражением экземпляра является ключевое слово Me, MyClass или MyBase. Выражение экземпляра, которое может использоваться только в теле необщедоступного метода, конструктора или метода доступа к свойству, классифицируется как значение .

9.2.5.2 Контрольные параметры

Если тип переменной передается с опорным параметром не совместим с типом эталонного параметра, или, если не-переменной передается в качестве аргумента для опорного параметра , временная переменная может быть распределены и переданы в опорный параметр. Передаваемое значение будет скопировано во временную переменную перед вызовом метода и будет скопировано обратно в исходную переменную (если она есть) при возврате метода.

(Весь акцент мой)

Итак, компилятор создаст временную переменную, присвоенную значению Me для передачи в качестве параметра ByRef. По возвращении копия полученного значения не выполняется, поскольку Me не является переменной.

5 голосов
/ 05 октября 2010

Похоже, компилятор преобразует "Я" в переменную, которая затем передается ByRef.Если вы скомпилируете свой код, а затем откроете его с помощью Reflector, вы увидите, что происходит:

Class Assigner
    ''// Methods
    Public Sub Assign(ByRef x As Assigner, ByVal y As Assigner)
        x = y
    End Sub

    Public Sub AssignNew()
        Dim VB$t_ref$S0 As Assigner = Me
        Me.Assign((VB$t_ref$S0), New Assigner)
    End Sub

    Public Function GetField() As Integer
        Return Me.Field
    End Function


    ''// Fields
    Public Field As Integer
End Class

Так что, когда вы вызываете AssignNew (), похоже, что вы присваиваете новый экземпляр внутренне сгенерированной переменной.Переменная «a» не трогается, потому что она даже не является частью функции.

1 голос
/ 05 октября 2010

Это лишь одна из тысяч возможных «почти ошибок», которые может сделать программист. MS поймал большинство из них, на самом деле, иногда я удивляюсь, сколько предупреждений появляется.

они пропустили это.

Насколько это не меняет меня, это чертовски хорошо! Когда вы используете «я», он просто передает копию реального класса, с которым вы работаете, в целях безопасности. Если бы это сработало так, как вы надеялись, мы бы говорили о побочном эффекте GIANT . Вы невинно работаете с методами своего класса, и они BAM внезапно оказываются в совершенно другом объекте! Это было бы ужасно! Если вы собираетесь это сделать, вы также можете просто написать часть кода MS-Basic spagetti со строковым номером со всеми глобальными переменными, которые устанавливаются случайным образом, и без подпрограмм / функций.

То же самое происходит, если вы передаете аргументы в скобках. Например, это работает как ожидалось:

Assign(Reference_I_Want_To_Set, New Assigner)

Но это ничего не меняет:

Assign((Reference_I_Want_To_Set), New Assigner)

Если вы отражаете код вышеупомянутого типа, как предлагает adam101, вы увидите похожие результаты. Хотя это очень расстраивает из-за скобок, это очень хорошо с Me !!!

0 голосов
/ 05 октября 2010

что нужно сделать, чтобы этот код работал, это:

Class Assigner
''// Ignore this for now.

Private newPropertyValue As Integer
Public Property NewProperty() As Integer
    Get
        Return newPropertyValue
    End Get
    Set(ByVal value As Integer)
        newPropertyValue = value
    End Set
End Property


''// This part is not so weird... take another instance ByRef,
''// assign it to a different instance -- stupid but whatever. '
Shared Sub Assign(ByRef x As Assigner, ByVal y As Assigner)
    x = y
End Sub

''// But... what's this?!?
Shared Sub AssignNew(ByRef x As Assigner)
    ''// Passing "Me" ByRef???
    Assign(x, New Assigner)
End Sub
End Class

затем используйте его как

    Dim a As New Assigner With {.NewProperty = 10}

    Assigner.AssignNew(a)

Насколько я понимаю, вы не можете изменить ссылку на объект во время его использования, поэтому вам нужно изменить его в общей подпрограмме


, поскольку Me не может быть целью назначения , кажется, что код создает его копию, и с этого момента вы используете не реальный объект, а копию из этого

...