如何使用 VB.NET 为字符串赋值?
Posted
技术标签:
【中文标题】如何使用 VB.NET 为字符串赋值?【英文标题】:How to assign value to string using VB.NET? 【发布时间】:2012-05-09 10:42:19 【问题描述】:考虑以下代码:
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 示例,但在此行 "Field.SetValue(Me, VariableValue)"
上出现“NullReferenceException”失败。请帮我解决问题。以下是我基于 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
【问题讨论】:
您通常不会这样做,即使在支持这种类型的语言中也是如此。您保留键值对的哈希表/字典,而不是普通变量。 @PanPizza 检查我的答案 - 我刚刚编辑了它。 好的,刚刚注意到您想根据变量的名称动态分配变量。对此也有解决方案,我刚刚再次修改了我的问题以反映这一点。 Pan Pizza 请使用 StringBuilder 而不是使用 Strings。 我也认为哈希表、字典或其他键=值对类型的数据结构是处理这些数据的更好方法。 【参考方案1】:您无法反映局部变量,因为编译后,编译器会丢失局部变量的名称,但会保留类级别的名称。
因此,要使您的示例正常工作,您需要将 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)
您必须记住的是,反射很昂贵。 除非万不得已,否则不要这样做。在这种情况下,我看不出真正需要它,而且我没有看到任何东西可以说服我你别无选择。尽量让您的解决方案尽可能简单。
【讨论】:
【参考方案2】:虽然您可以使用Reflection.MethodBody
类的LocalVariables
属性访问方法的局部变量,但没有设置值的好方法,因为变量名称没有存储在任何地方。所以你不能通过变量名的反射来查找。您可以通过索引/类型枚举局部变量,但没有明确的方法来判断哪个变量是哪个变量,除非您事先知道它们被声明的确切顺序(在这种情况下,您可以通过索引)。
您可以在此处找到有关我在 msdn 主题中所说内容的更多信息:
MethodBody.LocalVariables Property
如果您无论如何都需要通过反射查找局部变量,那么这是一个非常糟糕的设计,您应该在这部分工作更多(重构您的代码)。 您应该尽可能避免完全使用反射,因为它的开销很大并且非常慢。 Dictionary/SortedDictionary 方法在您所处的情况下是一种更好的方法。
【讨论】:
【参考方案3】:如果您将变量添加到变量的键控字典中,您可以使用 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)
【讨论】:
【参考方案4】:您可以使用reflection设置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 Reflection Handbook
反射是 .NET 提供的一种机制,使开发人员能够 使他们的计划更加灵活和动态。反射使它 应用程序可能更加模块化、可扩展和 可配置。建立在面向对象的基础之上 .NET 类型系统,反射提供了动态的机制 在运行时检查、修改甚至创建对象。 .NET 也 增加了程序员为其类型添加属性的能力, 提供关于可以检查和使用的类型的元数据 通过运行时的反射。
本书探讨了反射的所有使用方式,并确定了 实际应用和重要的编程技术依赖 在反思他们的功能。它涵盖了反射 API, .NET 中属性的使用,以及 .NET 的机制 提供代码的动态生成 - 所有技术允许 开发人员构建更灵活、更动态的应用程序。
【讨论】:
【参考方案5】:我希望我的问题是正确的。在我看来,好像您想将 temp(1)
的值分配给您之前声明的变量 S1
。
这是通过一个简单的操作完成的:
S1 = temp(1)
或者如果您不清楚,也可以使用Set
:
Set S1 = temp(1)
有关here 和here 的更多信息。
编辑 1
考虑到您刚刚作为对问题的评论所写的内容,您要拆分的字符串来自一个文件,该文件可能会在任何给定时间点发生变化。
为此,我会考虑使用数据库(带有触发器)或FileSystemWatcher
(文档here)对象来监视特定目录的文件更改。
使用它的示例解决方案如下所示。
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
的变量的值,那么您应该查看反射,它允许您在运行时评估代码,而不是编译时.
我在下面给了你一个示例代码,它几乎总结了反射的使用。
'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)
【讨论】:
Set
是 VB6,在 .NET 中没有任何意义,实际上 IDE 只会删除它。
即使在 VB6 中也不需要它。我把它完全放在那里,以便他发现它更具可读性时可以使用它。
+1,我在编辑 2 中使用反射尝试了您的示例,但 VS 在“Field.SetValue(Me, VariableValue)”这一行上说“NullReferenceException”。你能扩展你的答案吗?
很可能是因为找不到该字段。它可能是公共的,或者可能是局部变量而不是类变量,或者名称不正确。 BindingFlags 属性指定应该找到什么类型的字段。
@Mathias 不,这是错误的:在有效的情况下,VB6 需要Set
。它不需要Let
,但对象赋值的Set
必须消除对象赋值和隐式默认属性赋值之间的歧义。【参考方案6】:
如果您只想将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")
【讨论】:
他正在使用的数据来自一个一直在变化的文件,他希望他的变量也随之变化。我也对此感到困惑。【参考方案7】:如果我正确理解了您的问题,您希望将temp(1)
的值分配给与temp(0)
的值同名的局部变量。因此,如果您的文件包含S1=hello
、S2=world
,您希望您的代码将“hello”分配给变量S1
(如果存在这样的变量,将“world”分配给变量S2
)。
不幸的是,Visual Basic 不支持将值分配给名称在运行时确定的局部变量。如果S1
是类字段或属性,您可以使用reflection 或序列化库(例如XmlSerializer,但是它希望输入文件是XML 格式而不是名称=值对)来分配它。
一般来说,需要更多的上下文来建议适合您情况的最佳替代方案。例如,如果您只有名称S1
、...、S20
,我会使用array。如果您的键是任意名称,Dictionary 可能更合适。
【讨论】:
我同意。那次否决票不是很公平。这个问题甚至不清楚。我给了它一个 +1 让它回到正轨。 @MathiasLykkegaardLorenzen:谢谢!以上是关于如何使用 VB.NET 为字符串赋值?的主要内容,如果未能解决你的问题,请参考以下文章