【问题标题】:System.String to implement a custom InterfaceSystem.String 实现自定义接口
【发布时间】:2019-02-27 17:18:39
【问题描述】:

我有一个Interface,它定义了一组类,它们都实现了.ToString 成员。 我怎样才能让原生数据类型String 也实现这个Interface

首先我不确定我会怎么做,但有件事告诉我StringNotInheritable 让事情变得更加困难?

下面我的试探:(C#中的答案也可以接受)

Public Interface IString
    Function ToString() As String
End Interface

Partial Public Class String
    Implements IString

    Public Shadows Function ToString() As String Implements IString.ToString
        Return MyBase.ToString()
    End Function

End Class

我想要达到的目标

我的一个潜艇应该能够将多个类型作为输入,其中一个是原生的String 类型。

Sub DoSomething(MyData as IString)
    '... Do something using MyData.ToString, for example send it to a WebAPI
End Sub

Public Class SomeData
    Implements IString 'Sub DoSomething should accept this type
    Public Sub ToString() as String
        Return 'Something
    End Sub
End Class

Public Class SomeOtherData
    'Sub DoSomething should NOT accept this type
    Public Sub ToString() as String
        Return 'Something
    End Sub
End Class

我想避免这样做:

Sub DoSomething(MyData as Object)
    If not TypeOf MyData Is String or not TypeOf MyData Is SomeData Then Throw New ArgumentException()
    '...
End Sub

我想避免这样做:

Sub DoSomething(MyData as String)
    '...
End Sub

'Overload
Sub DoSomething(MyData as SomeData)
    call DoSomething(MyData.ToString)
End Sub

【问题讨论】:

  • 您能告诉我们您为什么要这样做吗? .NET 中的所有内容都已经有一个 ToString 方法(它位于 System.Object 上)
  • 您希望如何修改 String 类来实现任何东西?
  • 它与 this post 相关,我在其中创建了一个 Base64String 类型,以便我可以判断我的数据何时是 Unicode 字符串类型或 BytesEncodedIntoBase64 字符串类型。我的一些功能应该可以作为argument。所以我想创建一个包含两者的接口。我需要从Base64String 得到的只是.ToString,它返回一个String
  • @CamiloTerevinto 我不知道,这就是我在这里的原因:)
  • 你能更清楚地解释你想要做什么。字符串已经是字符串了,你想让ToString方法返回什么?

标签: c# .net vb.net


【解决方案1】:

以下是解决您困境的两种替代策略:

  1. 不用为不同类型的字符串创建不同的类,只需创建一个具有两个字段的类(例如 SpecialString)——一个字符串和一个枚举值来描述它包含的字符串类型(例如 Normal、Base64 , ETC)。然后,您可以将该类传递给需要能够接受这两种类型的字符串但仍然能够确定它是哪种类型的函数。您甚至可以编写从字符串到 SpecialString 的隐式转换(即Widening Operator CType),这样您甚至不需要为普通字符串显式创建 SpecialString。

  2. 如果要将不同类型的字符串保留为单独的类,可以创建一个实现 ToString 的基类(例如 AnyString)。然后创建Base64String 作为派生自AnyString 的类。您可以编写从System.StringAnyString 的隐式转换运算符。如果您想同时接受StringBase64String,请编写您的函数以接受AnyString。或者如果你想禁止普通的Strings,你可以只接受Base64String

策略一的示例代码:

Module Module1
    Enum StringType
        Normal
        Base64
        Base64DotBase64
    End Enum

    Class SpecialString
        Private type As StringType
        Private str As String

        Public Sub New(s As String, Optional type As StringType = StringType.Normal)
            Me.str = s
            Me.type = type
        End Sub

        Shared Widening Operator CType(ByVal s As String) As SpecialString
            Return New SpecialString(s, StringType.Normal)
        End Operator

        Public Overrides Function ToString() As String
            Return str
        End Function

        Public Function GetStringType() As StringType
            Return type
        End Function
    End Class

    'Function that uses SpecialString
    Sub Test(ss As SpecialString)
        ' Print StringType and inner value of string
        Console.WriteLine( ss.GetStringType().ToString("F") & ": " & ss.ToString())
    End Sub

    Sub Main()
        Dim ss1 As new SpecialString("abcdef", StringType.Base64)
        Dim ss2 As new SpecialString("abcdef:abcdef", StringType.Base64DotBase64)

        Test("Hello") 'Call with string
        Test(ss1) 'Call with Base64
        Test(ss2) 'Call with Base64DotBase64

        ' Pause to see the screen
        Console.ReadKey()
    End Sub

End Module

预期输出:

Normal: Hello 
Base64: abcdef 
Base64DotBase64: abcdef:abcdef

策略 2 的示例代码:

Imports System.Text.Encoding

Module Module1
    'Abstract base class to represent all string types (Normal and Base64 etc)
    MustInherit Class AnyString
        Private str As String
        Public Sub New(s As String)
            Me.str = s
        End Sub
        Public Overrides Function ToString() As String
            Return str
        End Function
        ' Allow implicit conversion of a System.String to NormalString, which inherits from AnyString
        Shared Widening Operator CType(ByVal s As String) As AnyString
            Return New NormalString(s)
        End Operator
    End Class

    'Class for Base64 strings only.
    Class Base64String
        Inherits AnyString
        Public Sub New(s As String)
            MyBase.New(s)
        End Sub
    End Class
    'Class for Normal strings. System.String implicitly converts to this.
    Class NormalString
        Inherits AnyString
        Public Sub New(s As String)
            MyBase.New(s)
        End Sub
        ' Allow implicit conversion of a System.String to NormalString
        ' This CType Operator isn't strictly necessary for this example, 
        ' because the CType in AnyString does the implict conversion shown below, 
        ' but it might be useful in general.
        Shared Widening Operator CType(ByVal s As String) As NormalString
            Return New NormalString(s)
        End Operator
    End Class

    'Function that Accepts Base64String OR Normal String
    Sub TestAny(s As AnyString)
        'Call ToString for whatever type of string was passed.
        Console.WriteLine(s.GetType().Name.ToString()  & ": " & s.ToString())

        'Also do something special for base64 string
        If TypeOf s Is Base64String then
            Console.WriteLine("Base64 Decoded (in TestAny): " & DecodeBase64(DirectCast(s,Base64String)))
        End If
    End Sub
    ' Function to convert Base64-encoded string to normal text. 
    ' This ONLY takes Base64Strings (not NormalStrings)
    Function DecodeBase64(s64 As Base64String) As String
        Return UTF8.GetString(System.Convert.FromBase64String(s64.ToString()))
    End Function

    Sub Main()
        'Normal String
        Dim s As new System.String("I am Normal")
        ' Base64String
        Dim s64 As New Base64String("SGVsbG8gV29ybGQh")

        'Call TestAny with any type of string
        TestAny("Hi") 'Call with string directly
        TestAny(s)    'Call with String object
        TestAny(s64)  'Call with Base64DotBase64

        'Call DecodeBase64 with a Base64String ONLY
        Console.Write("Base64-Decoded (in Main): ")
        Console.WriteLine(DecodeBase64(s64))   'OK call with Base64String
        'Console.WriteLine(DecodeBase64("Hi"))  !!! Invalid -- cannot call DecodeBase64 with string
        'Console.WriteLine(DecodeBase64(s))     !!! Invalid -- cannot call DecodeBase64 with string

        ' Pause to see the screen
        Console.ReadKey()
    End Sub

End Module

预期输出:

NormalString: Hi
NormalString: I am Normal
Base64String: SGVsbG8gV29ybGQh
Base64 Decoded (in TestAny): Hello World!
Base64-Decoded (in Main): Hello World!

【讨论】:

  • " 您甚至可以编写从字符串到 SpecialString 的隐式转换(即加宽运算符 CType)"。有趣的。我该怎么做?目前我的Base64String 通过Widening Operator Type 转换为String,但我虽然System.String 不能被触摸?
  • 在SpecialString类中可以定义加宽操作符,所以Strings可以自动转换为SpecialString。我会发布一些代码。
  • System.String是不能修改的,但是转换(CType)操作符可以在任意一个类上定义,所以可以 i> 将其添加到自定义的 SpecialString 类中(如上面的代码所示)。但正如其他人所说,您不能做任何事情来使 System.String 实现任何新接口。
  • 对不起,我有一段时间没有注意到敌人了。这里的问题是我们正在从将多个类型分组到一个接口中转变为将多个实例分组到一个类型中。这意味着不接受 IString 而只接受 Base64String 类型的公式将需要转换为“函数(s as Specialstring):如果 s.GetStringType x then Throw New Exception”。我们正在转移问题而没有真正解决它。我知道它无法真正解决,对我来说最好的方法是接口我的自定义类型并重载 String 类型。
  • 我也想避免使用选项 2,因为这意味着该函数会将字符串类型作为输入,这很容易误导程序员,因为他们可能无法轻易理解字符串可以是一个 Base64 字符串。对于少数人完全阅读函数的 XML 文档
【解决方案2】:

看看这是否有效:

重载DoSomething如下:

Sub DoSomething(MyData as String)
    '... Do something using MyData string, for example send it to a WebAPI
End Sub

Sub DoSomething(MyData as IString)
    DoSomething(MyData.ToString())
End Sub

当您使用 Native String 调用 DoSomething 时,将调用第一个方法。并且当您使用 IString 类型的对象调用 DoSomething 时,将调用第二个方法,该方法在内部调用第一个 DoSomething 方法,并将 IString.ToString() 值作为参数

【讨论】:

  • 这是解决方法(请参阅我的更新,我认为我们同时编写了它),但是根本不可能告诉本机类实现我可能已经创建的Interface我自己?
  • 我们如何在第三方库(阅读:.net 框架)中的类上实现接口?
  • "read: .net framework" 你能说得更具体点吗?我没有在文档中找到任何内容,因此我提出了问题。人们似乎认为这是无法实现的?
  • 这就是我的意思。正如其他人所说,我们不能为不属于我们的类实现接口。 string 类属于 .Net 库。
【解决方案3】:

在 C# 中

不,您不能修改.ToString() 函数的行为。

您可以做的一件事是使用扩展方法。但不幸的是,只有在没有匹配的适用候选方法时才会检查扩展方法。在调用ToString() 的情况下,总会有一个适用的候选方法,即object 上的ToString()。扩展方法的目的是扩展类型上可用的方法集,而不是覆盖现有方法;这就是为什么它们被称为“扩展方法”。如果要覆盖现有方法,则必须创建一个覆盖方法。因此,您不应该为 Extension 方法使用名称 ToString,因为它永远不会被调用,因为该方法已经存在并且您不应该使用 T,因为它在那里没用。

如何在 C# 中编写扩展方法

public static class ListHelper
{
    public static string ToMyString<T>(this IList<String> list)
    {
        return string.Join(", ", list.ToArray());
    }

    public static string ToMyString<T>(this String[] array)
    {
        return string.Join(", ", array);
    }
}

更新

您不能在第三方类上实现自定义接口。

【讨论】:

  • 谢谢哈比。我不想修改String类型的.ToString,我只想告诉自定义接口String实现了它。
  • @Ama 第三方库无法实现自定义接口。
  • 感谢 Habib,也许这是我从原始答案中需要的唯一回复!
猜你喜欢
  • 2018-07-17
  • 1970-01-01
  • 2016-09-12
  • 2020-05-02
  • 2021-01-10
  • 2023-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多