【问题标题】:Passing structure to a procedure as ByValue, however the procedure changes original structure values将结构作为 ByValue 传递给过程,但过程会更改原始结构值
【发布时间】:2014-02-06 07:21:26
【问题描述】:

我想问一些关于 VB.NET 代码的问题。我深受这个问题的困扰。如果您知道我的问题的解决方案,请回答我或给我一些 cmets。


'Declare a structure "Gene"
    Public Structure Gene
    Dim Seq() As Integer
    End Structure

'Here is a procedure, it changes original value.
        Public Sub Mutation(ByVal OriginalGene As Gene, ByRef TargetGene As Gene)
        Dim P1 As Integer
        Dim P2 As Integer
        Dim Temp As Integer

        P1 = Int((N_Jobs - 1 + 1) * Rnd(RndNum) + 0)
        P2 = Int((N_Jobs - 1 + 1) * Rnd(RndNum + 1) + 0)

        TargetGene.Seq = OriginalGene.Seq

        Temp = TargetGene.Seq(P1)
        TargetGene.Seq(P1) = TargetGene.Seq(P2)
        TargetGene.Seq(P2) = Temp
        End Sub

【问题讨论】:

  • Gene 是按值传递的,但是其中一个成员是对数组的引用,因此您是按值传递引用,它仍然是引用。假设我有一张纸,上面有一个帐号。我把纸复印给你(按价值传递) 你从账户里取钱。尽管我按价值通过了该文件,但我去帐户并看到钱已被提取。
  • 谢谢,因为你的比喻,我完全理解了这个问题。那我能做些什么来防止原始数组被改变呢?
  • 可以传副本,也可以传只读接口。

标签: vb.net structure byval


【解决方案1】:

实际上并没有改变结构。所有结构包含一个指向数组的指针。即使您可以拥有无​​限数量的结构副本,对数组的引用也始终相同。


不可变基因类示例:

Public Class Gene
    Private _sequence() As Integer
    Public Sub New(sequence() As Integer)
        _sequence = sequence
    End Sub
    Public Function GetSequence() As Integer()
        Return _sequence.Select(Function (x) x).ToArray()
    End Function
    Public Function Mutate() As Gene
        Dim sequence() As Integer = Me.GetSequence()

        Dim P1 As Integer
        Dim P2 As Integer
        Dim Temp As Integer

        P1 = Int((N_Jobs - 1 + 1) * Rnd(RndNum) + 0)
        P2 = Int((N_Jobs - 1 + 1) * Rnd(RndNum + 1) + 0)

        TargetGene.Seq = OriginalGene.Seq

        Temp = sequence(P1)
        sequence(P1) = sequence(P2)
        sequence(P2) = Temp

        Return New Gene(sequence)
    End Function
End Class

【讨论】:

  • 感谢您的回答!在我的程序中不更改 OriginalGene 该怎么办?
  • 您必须将数组设为私有,然后提供访问只读数据的方法。任何更改数组的代码都需要返回一个新结构/类,其中包含具有更新值的新数组。
  • 我很难完全理解。你能举个简单的例子吗?
  • 我添加了一个示例类。
  • 感谢您的回答。对学习VB.Net代码很有帮助。
【解决方案2】:

您需要实现结构的副本,尝试类似:

    Structure Gene
    Dim Seq() As Integer
    Public Function Clone() As Gene
        Dim mySt As Gene
        Array.Copy(Me.Seq, mySt.Seq, Me.Seq.Length)
        Return mySt
    End Function
End Structure

然后代替:

TargetGene.Seq = OriginalGene.Seq

使用

TargetGene.Seq = OriginalGene.Clone()

【讨论】:

    【解决方案3】:

    如果序列的长度是固定的,您可以使用fixed 数组声明来保存它,但固定数组需要“不安全”代码,这在某些情况下可能会出现问题。

    否则,结构真的没有很好的方法来封装具有值语义的非固定集合。如果希望结构传达用可变值语义封装集合的错觉,则必须让结构持有对数组或其他数据类型的私有引用,一旦引用存储到结构字段中,该引用就永远不会改变。如果要求结构更改包含的数据,它必须创建一个封装更改数据的新对象。请注意,任何将要进行的更改都必须在存储参考之前进行。

    让结构提供可变值语义的另一个困难是,结构上的实例方法无法指示它们是否可以安全地用于只读结构实例。实际上,不能在只读结构实例上使用结构方法或属性,但如果 C# 看到,例如

     someObject.propertyOfStructType.doSomething();
    

    它会默默地重写代码

     var temp = someObject.propertyOfStructType;
     temp.doSomething();
    

    有时这种替换是安全的。有时不是。虽然编译器应该很容易检查一个方法是否被标记为“不用于只读结构”属性并拒绝使用这些方法进行上述替换,但没有记录用于此目的的属性。因此,如果有人想提供一种安全的方法,例如将数组中的一系列值复制到值数组类型中,必须编写并调用该方法,如下所示:

    public static int LoadFromEnumerable(ref ValueArray<T> it, int startIndex, IEnumerable<T> source);
    
    int numElementsAdded = ValueArray<int>.LoadFromEnumerable(ref myValueArray, 4, myIntegers);
    

    与看起来更干净的相反

    public int LoadFromEnumerable(int startIndex, IEnumerable<T> source);
    
    myValueArray.LoadFromEnumerable(4, myIntegers);
    

    【讨论】:

    • 感谢您的回答。你给了我很多关于 VB.Net 的信息。
    猜你喜欢
    • 2018-04-17
    • 1970-01-01
    • 2011-03-03
    • 2019-11-09
    • 1970-01-01
    • 1970-01-01
    • 2012-08-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多