9

考虑以下代码:

    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?

我已经声明了一个名为 S1 的字符串。现在我想为 S1 分配新值。新的字符串值使用以下格式存储在文件中:[变量名][= 作为分隔符][字符串值]。检索存储在文件中的字符串变量名称和值后,如何将值分配给 S1?

笔记:

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

需要注意的是,带有数据的字符串来自一个可能会不时更改的文件!当文件发生变化时,我希望变量也发生变化。

进一步澄清

我需要一段代码,在处理像“S1=hello”这样的字符串时,代码将首先找到一个声明的变量(即S1),然后用“hello”字符串分配S1变量。“=”只是作为变量名和变量值的分隔符。

更新

我尝试使用 Mathias Lykkegaard Lorenzen 的 EDIT 2 示例,但在此行上出现“NullReferenceException”失败"Field.SetValue(Me, VariableValue)"。请帮我解决问题。以下是我基于 Mathias Lykkegaard Lorenzen 的 EDIT 2 示例的代码:

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”分配给S2变量)。

不幸的是,Visual Basic 不支持为名称在运行时确定的局部变量赋值。如果S1是类字段或属性,您可以使用反射或序列化库(例如XmlSerializer,但是它希望输入文件是 XML 格式而不是名称=值对)来分配它。

一般来说,需要更多的上下文来建议适合您情况的最佳替代方案。例如,如果您只有 names S1, ..., S20,我会使用array。如果您的键是任意名称,则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

如果您希望能够更改给定变量名称为 a 的变量的值string,那么您应该查看反射,它允许您在运行时而不是编译时评估代码。

我在下面给了你一个示例代码,它几乎总结了反射的使用。

'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)


您必须记住的是,反射很昂贵。 除非绝对必要,否则不要这样做。 在这种情况下,我看不出对它的真正需求,而且我没有看到任何东西可以说服我你别无选择。尝试使您的解决方案尽可能简单。

于 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

如果您只想分配 to 的值temp(1)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 回答