9

Consider following code:

    Dim S1 As String = "a"

    'this is the string in a file
    Dim StringFromFile As String = "S1=hello"

    Dim temp() As String = Split(StringFromFile, "=", -1, CompareMethod.Binary)
    'temp(0) = variable name
    'temp(1) = variable value

    'my question is: how to assign value to S1?

I have declared a string named S1. Now I want to assign new value to S1. The new string value is stored in a file using following format: [variable name][= as separator][string value]. How do I assign the value to S1 after retrieving the string variable name and value that stored in a file?

NOTE:

temp(0) = "S1"
temp(1) = "hello"

It should be noted that the string with the data comes from a file that may change from time to time! When the file changes, I want the variables to change as well.

Further clarification

I need a piece of code that when processing a string like this "S1=hello", the code will first find a declared variable (i.e. S1), and then assign the S1 variable with "hello" string. The "=" just acted as separator for variable name and variable value.

UPDATE:

My attempt to use Mathias Lykkegaard Lorenzen's EDIT 2 example but failed with "NullReferenceException" on this line "Field.SetValue(Me, VariableValue)". Please help me fix the problem. Following is my code based on Mathias Lykkegaard Lorenzen's EDIT 2 example:

Public Sub Ask()
    Try
        Dim S1 As String = "a"

        Dim StringFromFile As String = "S1=hello"

        Dim temp() As String = Split(StringFromFile, "=", -1, CompareMethod.Binary)
        'temp(0) = variable name
        'temp(1) = variable value

        'my question is: how to assign value to S1?
        Dim TypeOfMe As Type = Me.GetType()

        'did this for readability.
        Dim VariableName As String = temp(0)
        Dim VariableValue As String = temp(1)

        'get the field in the class which is private, given the specific name (VariableName).
        Dim Field As FieldInfo = TypeOfMe.GetField(VariableName, BindingFlags.NonPublic Or BindingFlags.Instance)

        'set the value of that field, on the object "Me".
        Field.SetValue(Me, VariableValue) '<-- this line caused NullReferenceException
        MessageBox.Show(S1)
    Catch ex As Exception
        MessageBox.Show(ex.ToString)
    End Try

End Sub
4

7 に答える 7

7

リフレクションを使用して S1 値を設定できます。

Imports System.Reflection

Public Class Form1
    Public S1 As String = "a"

    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        Dim StringFromFile As String = "S1=hello"

        Dim temp() As String = Split(StringFromFile, "=", -1, CompareMethod.Binary)
        'temp(0) = variable name
        'temp(1) = variable value

        SetS1(Me, "S1", temp(1))
        MessageBox.Show(S1)
    End Sub

    ''' <summary>
    ''' 
    ''' </summary>
    ''' <param name="obj">the class that stores your S1 public field</param>
    ''' <param name="fieldName">that is your S1 field</param>
    ''' <param name="Value">S1 new value, that is hello</param>
    ''' <remarks></remarks>
    Public Sub SetS1(ByVal obj As Object, ByVal fieldName As String, ByVal Value As Object)
        Try
            Dim fi As FieldInfo = obj.GetType().GetField(fieldName, BindingFlags.Instance Or BindingFlags.Public Or BindingFlags.NonPublic)
            If Not fi Is Nothing Then
                fi.SetValue(obj, Value)
            End If

        Catch ex As Exception

        End Try
    End Sub
End Class

追加リソース

非常に優れたリフレクションに関する本があります。

Visual Basic .NET リフレクション ハンドブック

リフレクションは、.NET によって提供されるメカニズムであり、開発者がプロ​​グラムをより柔軟かつ動的にすることを可能にします。リフレクションにより、アプリケーションをよりモジュール化、拡張可能、および構成可能にすることができます。オブジェクト指向と .NET 型システムの基本に基づいて構築されたリフレクションは、実行時にオブジェクトを動的に検査、変更、さらには作成するためのメカニズムを提供します。.NET では、プログラマーが型に属性を追加する機能も追加されています。これにより、実行時にリフレクションを通じて調べて使用できる型に関するメタデータが提供されます。

この本では、リフレクションを使用できるすべての方法を検討し、機能をリフレクションに依存する実用的なアプリケーションと重要なプログラミング手法を特定します。リフレクション API、.NET での属性の使用について説明し、コードの動的生成のために .NET が提供するメカニズム (開発者がより柔軟で動的なアプリケーションを構築できるようにするすべての手法) についても説明します。

于 2012-05-02T07:39:36.693 に答える
3

私があなたの質問を正しく理解していれば、 の値temp(1)と同じ名前のローカル変数にの値を代入する必要がありtemp(0)ます。そのため、ファイルにS1=hello,が含まれている場合S2=world、コードで "hello" を変数に割り当てますS1(そのような変数が存在する場合は "world" を variableに割り当てS2ます)。

残念ながら、Visual Basic は、名前が実行時に決定されるローカル変数への値の割り当てをサポートしていません。S1クラス フィールドまたはプロパティの場合は、リフレクションまたはシリアル化ライブラリを使用して割り当てることができます (たとえば、 XmlSerializerは、入力ファイルが name=value ペアではなく XML 形式であることを想定しています)。

一般に、状況に最適な代替案を提案するには、もう少しコンテキストが必要です。たとえば、名前がS1, ...,だけの場合は、配列S20を使用します。キーが任意の名前である場合は、Dictionaryの方が適している可能性があります。

于 2012-04-29T11:37:24.987 に答える
3

質問が正しいことを願っています。以前に宣言しtemp(1)た変数にの値を代入したいように思えます。S1

これは、次の簡単な操作で実行できます。

S1 = temp(1)

または、それが明確でない場合は、Set同様に使用できます:

Set S1 = temp(1)

詳細については、こちらこちらをご覧ください。

編集 1

質問へのコメントとして書いたことを反映して、分割している文字列は、特定の時点で変更される可能性のあるファイルからのものです。

そのために、データベース(トリガー付き)またはFileSystemWatcher(ドキュメントはこちら)オブジェクトを使用して、特定のディレクトリのファイル変更を監視することを検討します。

これを使用したサンプル ソリューションを以下に示します。

Dim watcher As New System.IO.FileSystemWatcher()
watcher.Path = "C:\MyPathToMonitor"
watcher.Filter = "*MyFileNameToLookFor" 'could be *MyFile.txt

'assign the event. could be done by declaring the watcher `WithEvents` in the class scope too.
AddHandler watcher.Changed, AddressOf OnChanged

次に、イベント ハンドラーで次のようにします。

Private Shared Sub OnChanged(source As Object, e As FileSystemEventArgs)

    'here we just say that StringFromFile has been assigned to the file contents now.

    Dim temp() As String = Split(StringFromFile, "=", -1, CompareMethod.Binary)

    'remember to have the S1 variable in your class scope.
    S1 = temp(1)

End Sub 

編集 2

変数の名前が として与えられた変数の値を変更できるようにしたい場合は、stringコンパイル時ではなく実行時にコードを評価できるリフレクションを検討する必要があります。

以下に、Reflection の使用法をほぼまとめたサンプル コードを示します。

'get the reflection type of the current class.
Dim TypeOfMe As Type = Me.GetType()

'did this for readability.
Dim VariableName = temp(0)
Dim VariableValue = temp(1)

'get the field in the class which is private, given the specific name (VariableName).
Dim Field As FieldInfo = TypeOfMe.GetField(VariableName, BindingFlags.NonPublic Or BindingFlags.Instance)

'set the value of that field, on the object "Me".
Field.SetValue(Me, VariableValue)
于 2012-04-29T11:54:02.893 に答える
2

コンパイル後、コンパイラはローカルの名前を失いますが、クラス レベルの名前は保持するため、ローカル変数を反映することはできません。

したがって、例を機能させるには、 S1 をメソッドの内部変数としてではなく、クラスレベルの変数にする必要があります。

Public Class Class1
    Dim S1 As String = "a"

    Public Sub Ask()
        Try
            Dim StringFromFile As String = "S1=hello"

            Dim temp() As String = Split(StringFromFile, "=", - 1, CompareMethod.Binary)
            'temp(0) = variable name
            'temp(1) = variable value

            'my question is: how to assign value to S1?
            Dim TypeOfMe As Type = Me.GetType()

            'did this for readability.
            Dim VariableName As String = temp(0)
            Dim VariableValue As String = temp(1)

            'get the field in the class which is private, given the specific name (VariableName).
            Dim Field As FieldInfo = TypeOfMe.GetField(VariableName, BindingFlags.NonPublic Or BindingFlags.Instance)

            'set the value of that field, on the object "Me".
            Field.SetValue(Me, VariableValue)            '<-- this line caused NullReferenceException (no more.)
            Console.WriteLine("S1 using reflection")
            Console.WriteLine(S1)

        Catch ex As Exception
            Console.WriteLine(ex.ToString)
        End Try
    End Sub
End Class

ただし、ここですでに述べたように、はるかに実用的なアプローチは、辞書を使用することです。

Public Class Class2

    Public Sub Ask()
        Try
            Dim StringFromFile As String = "S1=hello"

            Dim temp() As String = Split(StringFromFile, "=", - 1, CompareMethod.Binary)

            ' the way you probably should do it.
            Dim test As Dictionary(Of string, string) = New Dictionary(Of String,String)()
            test.Add(temp(0), temp(1))
            Console.WriteLine("S1 using dictionary")
            Console.WriteLine(test("S1"))

        Catch ex As Exception
            Console.WriteLine(ex.ToString)
        End Try
    End Sub
End Class

目的の値が既に含まれている辞書がある場合は、もちろん追加を行うことはなく、次のようにキーを新しい値に設定するだけです。test(temp(0)) = temp(1)


覚えておかなければならないことは、Reflection はコストがかかるということです。 絶対に必要でない限り、それをしないでください。 この例では、それが本当に必要なのかわかりませんし、他に選択肢がないことを納得させるものは何も見ませんでした. ソリューションをできるだけシンプルに保つようにしてください。

于 2012-05-03T13:27:59.570 に答える
2

LocalVariablesクラスのプロパティを使用してメソッドのローカル変数にアクセスできますがReflection.MethodBody、変数名はどこにも保存されないため、値を設定する良い方法はありません。したがって、変数名によるリフレクションを介して検索することはできません。インデックス/タイプによってローカル変数を列挙することはできますが、変数が宣言されている正確な順序を事前に知っていることを除いて、どの変数がどれであるかを判断する決定的な方法はありません (この場合、インデックスで行くことができます)。

私が言っていることの詳細については、次の msdn トピックを参照してください。

MethodBody.LocalVariables プロパティ

とにかくリフレクションを介してローカル変数を検索する必要がある場合、それはまったく悪い設計であり、その部分でもっと作業する必要があります (コードのリファクタリング)。リフレクションはオーバーヘッドが大きく、非常に遅いため、可能な限りリフレクションの使用を避ける必要があります。Dictionary/SortedDictionary アプローチは、現在の状況に対応するためのはるかに優れた方法です。

于 2012-05-09T02:23:39.627 に答える
0

変数をキー付きディクショナリに追加する場合、set を使用してキーと値のペアの値を設定できます。これをチェックしてください:

Dim dctHolder As New Dictionary(Of String, Object)
Dim S1 As String
Dim tmp() As String = {"S1","Split Up Value"}
dctHolder.Add("S1",S1)
Set dctHolder(tmp(0)) = tmp(1)
于 2012-05-08T15:12:29.040 に答える
-2

temp(1)toの値のみを割り当てたい場合は、次のS1ようにできます。

S1 = temp(1)

可能な値の固定された小さなセットがある場合は、次を使用できますSelect Case

Select Case temp(0)
    Case "S1"
        S1 = temp(1)
    Case "S2"
        S2 = temp(1)
End Select

しかし、一般的に、これを行う最善の方法は、変数ではなく辞書を使用して値を保持することです。

Dim dict = New Dictionary(Of String, String)

dict(temp(0)) = temp(1)

S1次に、次のような方法で の値を取得できます。

dict("S1")
于 2012-04-29T11:58:35.850 に答える