.net 如何通过反射检索字符串并将其连接起来
我有一个长字符串,使用以下模式将其划分为许多较小的字符串:.net 如何通过反射检索字符串并将其连接起来,.net,vb.net,string,.net,Vb.net,String,我有一个长字符串,使用以下模式将其划分为许多较小的字符串: Public Class Test Public Prefix_1 as String = "1 to 100 bytes" Public Prefix_2 as String = "101 to 200 bytes" Public Prefix_3 as String = "201 to 300 bytes" Public Prefix_4 as String = "301 to 400 bytes"
Public Class Test
Public Prefix_1 as String = "1 to 100 bytes"
Public Prefix_2 as String = "101 to 200 bytes"
Public Prefix_3 as String = "201 to 300 bytes"
Public Prefix_4 as String = "301 to 400 bytes"
'and so on
End Class
这个测试类
已编译为类库项目(即.dll文件),并保存到C:\Test.dll
请注意,我事先不知道dll文件中存在多少前缀
字符串
我的问题是:如何通过反射检索以前缀开头的所有字符串
,并将其升序连接(即前缀1和前缀2…)为单个字符串
赏金更新:
奖励仅适用于VB.NET解决方案中的答案您有公共字段,因此,从表示类的
类型
对象获取字段信息
对象,并排除名称不以前缀开头的对象
一旦有了这些,您就可以调用FieldInfo
对象上的GetValue
,将对象(类Test
的实例)作为参数来获取字段的值
如果您需要按顺序排列结果,那么我建议使用LINQ语句
对不起,我不懂VB,否则我会给你写一些代码
更新:一些C#代码
应该就这些了。这应该让你开始了。抱歉,这是C#,但我不记得lambda语法
Type type = Assembly.LoadFrom (@"c:\test.dll").GetType ("Test");
object instance = type.GetConstructor (Type.EmptyTypes).Invoke (null);
var fields = type.GetFields ().Where (f => f.Name.StartsWith ("Prefix_")).OrderBy(f => f.Name);
string x = fields.Aggregate (new StringBuilder (), (sb, f) => sb.Append((string)f.GetValue (instance)), sb => sb.ToString ());
VB.NET
Dim type As Type = Assembly.LoadFrom("c:\test.dll").GetType("Test")
Dim instance As Object = Type.GetConstructor(Type.EmptyTypes).Invoke(Nothing)
Dim fields = _
type.GetFields() _
.Where(Function(f) f.Name.StartsWith("Prefix_")) _
.OrderBy(Function(f) f.Name)
Dim bigString As String = _
fields.Aggregate(New StringBuilder(), _
Function(sb, f) sb.Append(DirectCast(f.GetValue(instance), String)), _
Function(sb) sb.ToString())
是用C代码编写的(VB.NET有点生疏了:):
我想根据您的回答,按照您的要求,用Visual Basic提出一个面向对象的解决方案 免责声明: 请记住,我不是VB.NET开发人员。我提供的代码经过测试并正在运行,但肯定需要一些特定于语言的改进 我假设您正在使用类
Test
的实例,因为它公开的字段不是共享的
主旨
通过分析您的需求,我发现这对于:
- 将所有字段的命名策略集中在一个位置,以使您的解决方案可维护
- 注意字段的排序。如果您有前缀_1、前缀_2和前缀_11,则可能会以错误的方式进行排序:前缀_1、前缀_11和前缀_2
- 验证是否没有缺少字段名(即从前缀_1跳到前缀_3)
StringChunkField
的类中对每个包含字符串块的字段进行建模。
该类为每个包含字符串块的前缀字段建模,并承担以下职责:
- 提供有关字段本身的信息:名称、编号、包含的字符串块以及其中的字符数
- 集中有关用于命名字段的格式和编号的信息。这里定义了要查找的前缀,并且在处的数字以字段名称开始
- 从上一项中,它可以回答字段是否是字符串开头的字段,以及字段是否是StringChunkField
- 实现IComparable以将排序逻辑集中在一个位置(基于字段编号)
StringChunkField
、使用什么名称前缀以及如何构建前缀,我们可以通过TypeEmbeddedStringReader
类的实例查询对象中包含的字符串
其责任是:
- 查找对象中显示的所有
StringChunkFields
- 验证找到的字段编号是否根据
中定义的基数开始,以及编号是否连续StringChunkField
- 从
s值重建对象中嵌入的字符串StringChunkField
Imports System.Reflection Imports System.Text Public Class TypeEmbeddedStringReader Public Shared Function ReadStringFrom(ByRef target As Object) As String ' Get all prefix fields from target ' Each StringChunkField hold a chunk of the String to rebuild Dim prefixFields As IEnumerable(Of StringChunkField) = GetPrefixFieldsFrom(target) ' There must be, at least, one StringChunkField ValidateFieldsFound(prefixFields) ' The first StringChunkField must hold the beggining of the string (be numbered as one) ValidateFieldNumbersBeginAtOne(prefixFields) ' Ensure that no StringChunkField number were skipped ValidateFieldNumbersAreConsecutive(prefixFields) ' Calculate the total number of chars of the string to rebuild to initialize StringBuilder and make it more efficient Dim totalChars As Integer = CalculateTotalNumberOfCharsIn(prefixFields) Dim result As StringBuilder = New StringBuilder(totalChars) ' Rebuild the string For Each field In prefixFields result.Append(field.StringChunk) Next ' We're done Return result.ToString() End Function #Region "Validation" Private Shared Sub ValidateFieldsFound(ByVal fields As List(Of StringChunkField)) If (fields.Count = 0) Then Throw New ArgumentException("Does not contains any StringChunkField", "target") End Sub Private Shared Sub ValidateFieldNumbersBeginAtOne(ByVal fields As List(Of StringChunkField)) ' Get the first StringChunkField found Dim firstStringChunkField As StringChunkField = fields.First ' If does not holds the begining of the string... If (firstStringChunkField.HoldsBeginingOfTheString() = False) Then ' Throw an exception with a meaningful error message Dim invalidFirstPrefixField = String.Format("The first StringChunkField found, '{0}', does not holds the beggining of the string. If holds the beggining of the string, it should be numbered as '{1}'.", firstStringChunkField.Name, StringChunkField.BeginningOfStringFieldNumber) Throw New ArgumentException(invalidFirstPrefixField, "target") End If End Sub Private Shared Sub ValidateFieldNumbersAreConsecutive(ByVal fields As List(Of StringChunkField)) For index = 0 To fields.Count - 2 ' Get the current and next field in fields Dim currentField As StringChunkField = fields(index) Dim nextField As StringChunkField = fields(index + 1) ' If the numbers are consecutive, continue checking If (currentField.IsFollowedBy(nextField)) Then Continue For ' If not, throw an exception with a meaningful error message Dim missingFieldMessage As String = String.Format("At least one StringChunkField between '{0}' and '{1}' is missing", currentField.Name, nextField.Name) Throw New ArgumentException(missingFieldMessage, "target") Next End Sub #End Region Private Shared Function CalculateTotalNumberOfCharsIn(ByVal fields As IEnumerable(Of StringChunkField)) As Integer Return fields.Sum(Function(field) field.NumberOfCharacters) End Function Private Shared Function GetPrefixFieldsFrom(ByVal target As Object) As List(Of StringChunkField) ' Find all fields int the target object Dim fields As FieldInfo() = target.GetType().GetFields() ' Select the ones that are PrefixFields Dim prefixFields As IEnumerable(Of StringChunkField) = From field In fields Where StringChunkField.IsPrefixField(field) Select New StringChunkField(field, target) ' Return the sorted list of StringChunkField found Return prefixFields.OrderBy(Function(field) field).ToList() End Function End Class
TypeEmbeddedStringReader
类的行为和使用方法。
简单地说,您必须调用Shared
函数ReadStringFrom
作为参数传递包含要读取的字符串的对象
以下是示例类型:
Public Class SampleType
Public Prefix_1 As String = "1 to 100 bytes"
Public Prefix_2 As String = "101 to 200 bytes"
Public Prefix_3 As String = "201 to 300 bytes"
Public Prefix_4 As String = "301 to 400 bytes"
End Class
Public Class TypeWithoutString
End Class
Public Class TypeWithNonConsecutiveFields
Public Prefix_1 As String = "1 to 100 bytes"
Public Prefix_5 As String = "101 to 200 bytes"
End Class
Public Class TypeWithInvalidStringBeginning
Public Prefix_2 As String = "1 to 100 bytes"
End Class
下面是我用来测试它的主要模块:
Imports TypeEmbeddedStringReader.Samples
Module Module1
Sub Main()
ExtractStringFrom(New TypeWithoutString())
ExtractStringFrom(New TypeWithInvalidStringBeginning())
ExtractStringFrom(New TypeWithNonConsecutiveFields())
ExtractStringFrom(New SampleType())
End Sub
Private Sub ExtractStringFrom(ByVal target As Object)
Try
Dim result As String = TypeEmbeddedStringReader.ReadStringFrom(target)
Console.WriteLine(result)
Catch exception As ArgumentException
Console.WriteLine("Type '{0}': {1}", target.GetType(), exception.Message)
End Try
Console.WriteLine()
End Sub
End Module
以及运行它的结果:
Type 'TypeEmbeddedStringReader.Samples.TypeWithoutString': Does not contains any StringChunkField
Parameter name: target
Type 'TypeEmbeddedStringReader.Samples.TypeWithInvalidStringBeginning': The first StringChunkField found, 'Prefix_2', does not holds the beggining of the string. If holds the beggining of the string, it should be numbered as '1'.
Parameter name: target
Type 'TypeEmbeddedStringReader.Samples.TypeWithNonConsecutiveFields': At least one StringChunkField between 'Prefix_1' and 'Prefix_5' is missing
Parameter name: target
1 to 100 bytes101 to 200 bytes201 to 300 bytes301 to 400 bytes
请让我知道是否为你工作,如果我可以为你提供任何其他帮助
更新
根据Gens的请求,我向TypeEmbeddedStringReader
类添加了一个函数,用于从提供名称和程序集文件的类型实例中读取字符串:
Public Shared Function ReadStringFromInstanceOf(ByRef assemblyFile As String, ByRef targetTypeName As String)
Dim assembly As Assembly = assembly.LoadFrom(assemblyFile)
Dim targetType As Type = assembly.GetType(targetTypeName)
Dim target As Object = Activator.CreateInstance(targetType)
Return ReadStringFrom(target)
End Function
以下是我用于测试的样本类型:
Public Class UnorderedFields
Public Prefix_2 As String = "101 to 200 bytes"
Public Prefix_4 As String = "301 to 400 bytes"
Public Prefix_1 As String = "1 to 100 bytes"
Public Prefix_3 As String = "201 to 300 bytes"
End Class
下面是测试它的代码:
Dim assemblyFile As String = Assembly.GetExecutingAssembly()
Dim targetTypeName As String = "TypeEmbeddedStringDemo.UnorderedFields"
Console.WriteLine(TypeEmbeddedStringReader.ReadStringFromInstanceOf(assemblyFile, targetTypeName))
这是上述代码的输出:
1 to 100 bytes101 to 200 bytes201 to 300 bytes301 to 400 bytes
我希望这能帮助你解决你的问题。如果你还需要什么,请告诉我
更新2
回答Gens,Simon的解决方案不起作用的原因是因为正在对字段名进行比较。下面的示例排序失败(只是为了说明排序问题,而且它是无效的)
它给出:
1 to 100 bytes**301 to 400 bytes**101 to 200 bytes201 to 300 bytes
正在修复比较器实现中使用数字而不是名称的问题:
Public Function Compare(ByVal x As FieldInfo, ByVal y As FieldInfo) As Integer Implements IComparer(Of FieldInfo).Compare
Dim xNumber = Integer.Parse(x.Name.Replace("Prefix_", String.Empty))
Dim yNumber = Integer.Parse(y.Name.Replace("Prefix_", String.Empty))
Return xNumber.CompareTo(yNumber)
End Function
给出正确的结果:
1 to 100 bytes101 to 200 bytes201 to 300 bytes301 to 400 bytes
希望有帮助。如果字符串的定义顺序与问题中的顺序相同,您可以避免排序,下面是一个简单的VB.NET答案:
Public Function Extract() As String
Dim type As Type = Assembly.LoadFrom("C:\test.dll").GetType("YourNamespace.Test")
Dim instance As Object = Activator.CreateInstance(type)
Dim sb As New StringBuilder
Dim field As FieldInfo
For Each field In type.GetFields
If field.Name.StartsWith("Prefix_") Then
sb.Append(field.GetValue(instance))
End If
Next
Return sb.ToString
End Function
否则,这里有一个带有排序的函数:
Public Function Extract() As String
Dim type As Type = Assembly.LoadFrom("c:\test.dll").GetType("YourNamespace.Test")
Dim fields As New List(Of FieldInfo)
Dim field As FieldInfo
For Each field In type.GetFields
If field.Name.StartsWith("Prefix_") Then
fields.Add(field)
End If
Next
fields.Sort(New FieldComparer)
Dim sb As New StringBuilder
Dim instance As Object = Activator.CreateInstance(type)
For Each field In fields
sb.Append(field.GetValue(instance))
Next
Return sb.ToString
End Function
Private Class FieldComparer
Implements IComparer(Of FieldInfo)
Public Function Compare(ByVal x As FieldInfo, ByVal y As FieldInfo) As Integer Implements IComparer(Of FieldInfo).Compare
Return x.Name.CompareTo(y.Name)
End Function
End Class
使用您的问题中稍微修改的测试类:
Public Class Test
Public Prefix_15 As String = "501 to 600 bytes"
Public Prefix_5 As String = "401 to 500 bytes"
Public Prefix_1 As String = "1 to 100 bytes"
Public Prefix_2 As String = "101 to 200 bytes"
Public Prefix_3 As String = "201 to 300 bytes"
Public Prefix_4 As String = "301 to 400 bytes"
End Class
运行以下功能:
Public Function GetPrefixString() As String
Dim type As Type = Assembly.LoadFrom("C:\test.dll").GetType("Test.Test")
Dim test As Object = Activator.CreateInstance(type)
Dim fieldList As New List(Of String)
For Each field As FieldInfo In _
From x In type.GetFields _
Where x.Name.StartsWith("Prefix_") _
Order By Convert.ToInt32(x.Name.Replace("Prefix_", String.Empty))
fieldList.Add(field.GetValue(test))
Next
Return String.Join(String.Empty, fieldList.ToArray)
End Sub
产生以下结果:
1到100字节101到200字节201到
300字节301到400字节401到500
字节501到600字节
嗨,Gens!我刚刚发布了你问题的答案。根据您所说的,我假设您必须使用类测试的实例,因为它公开的字段不是共享的。是这样吗?如果没有,请告诉我,以便我可以更新我的答案。谢谢为什么?
Public Function Extract() As String
Dim type As Type = Assembly.LoadFrom("C:\test.dll").GetType("YourNamespace.Test")
Dim instance As Object = Activator.CreateInstance(type)
Dim sb As New StringBuilder
Dim field As FieldInfo
For Each field In type.GetFields
If field.Name.StartsWith("Prefix_") Then
sb.Append(field.GetValue(instance))
End If
Next
Return sb.ToString
End Function
Public Function Extract() As String
Dim type As Type = Assembly.LoadFrom("c:\test.dll").GetType("YourNamespace.Test")
Dim fields As New List(Of FieldInfo)
Dim field As FieldInfo
For Each field In type.GetFields
If field.Name.StartsWith("Prefix_") Then
fields.Add(field)
End If
Next
fields.Sort(New FieldComparer)
Dim sb As New StringBuilder
Dim instance As Object = Activator.CreateInstance(type)
For Each field In fields
sb.Append(field.GetValue(instance))
Next
Return sb.ToString
End Function
Private Class FieldComparer
Implements IComparer(Of FieldInfo)
Public Function Compare(ByVal x As FieldInfo, ByVal y As FieldInfo) As Integer Implements IComparer(Of FieldInfo).Compare
Return x.Name.CompareTo(y.Name)
End Function
End Class
Public Class Test
Public Prefix_15 As String = "501 to 600 bytes"
Public Prefix_5 As String = "401 to 500 bytes"
Public Prefix_1 As String = "1 to 100 bytes"
Public Prefix_2 As String = "101 to 200 bytes"
Public Prefix_3 As String = "201 to 300 bytes"
Public Prefix_4 As String = "301 to 400 bytes"
End Class
Public Function GetPrefixString() As String
Dim type As Type = Assembly.LoadFrom("C:\test.dll").GetType("Test.Test")
Dim test As Object = Activator.CreateInstance(type)
Dim fieldList As New List(Of String)
For Each field As FieldInfo In _
From x In type.GetFields _
Where x.Name.StartsWith("Prefix_") _
Order By Convert.ToInt32(x.Name.Replace("Prefix_", String.Empty))
fieldList.Add(field.GetValue(test))
Next
Return String.Join(String.Empty, fieldList.ToArray)
End Sub