.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
    中定义的基数开始,以及编号是否连续
  • StringChunkField
    s值重建对象中嵌入的字符串

    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