8

介绍

这个问题是由 Marc Gravell 的建议提出的,我向该站点发布新的语言功能建议以收集对它们的一般意见。

这个想法是收集他们是否可能有帮助,或者也许已经有另一种方法来实现我所追求的。

建议(约束类型)

VB.Net 中的普通变量声明是这样编写的:

Dim SomeVariable as SomeType

我建议允许以下表格

Dim SomeVariable1 as {SomeType, ISomeInterface}
Dim SomeVariable2 as {SomeType, ISomeInterface, ISomeOtherInterface}

这种语法是从 Vb.Net 的约束泛型风格中借来的

为什么允许这样做?...它有什么用?

那么我最初想到的具体情况是定义一个特定的控件子集。我希望为一系列控制工厂创建一个接口,这些工厂将根据一些业务规则提供控制。

这些控件的使用者将通过接口要求创建的所有控件还应该实现一些接口系列(在我的例子中只有一个),这些接口为所有这些控件提供了通常在普通控件中没有的额外设施。

值得注意的是,以下内容目前不起作用。

Public Interface ISpecialControl
End Interface

Public Interface IControlProvider
    Function CreateControl(Of T As {Control, ISpecialControl})() As T
End Interface

Public Class SpecialTextBoxProvider
    Implements IControlProvider
    Public Function CreateControl(Of T As {Control, ISpecialControl})() As T Implements IControlProvider.CreateControl
        Return New SpecialTextBox
    End Function
End Class

Public Class SpecialTextBox
    Inherits TextBox
    Implements ISpecialControl
    Public Sub New()

    End Sub
End Class

我认为这转换为 C# 为:

public interface ISpecialControl
{
}

public interface IControlProvider
{
    T CreateControl<T>()
        where T : Control, ISpecialControl;
}

public class SpecialTextBoxProvider : IControlProvider
{
    public T CreateControl<T>()
        where T : Control, ISpecialControl
    {
        return new SpecialTextBox();
    }
}

public class SpecialTextBox : TextBox, ISpecialControl
{
}

由于无法将 SpecialTextbox 转换为 T,返回“New SpecialTextbox”的尝试失败。

"Value of type 'MyApp.SpecialTextBox' cannot be converted to 'T'"

我意识到我的工厂可以被允许返回简单的控件,如果它们实现了 ISpecialControl,我可以在运行时检查,但这会产生运行时问题,我宁愿在编译时检查,因为即使目前不是一个实际的可能性,这也是一种合乎逻辑的可能性

更新:想法是这些工厂可以位于外部(甚至可能是第三方)程序集中,并且可以依赖于他们想要的任何控件库,创建和返回这些控件的派生类,这些派生类也实现了 ISpecialControl。

这些程序集可以通过自配置反射(第一次通过的反射,然后是配置,然后在进一步运行中使用)定位,并且调用程序集在不知道这些控件将采取何种依赖关系的情况下使用这些程序集。

它确实要求这些工厂是可构造的,而无需传递有关它们期望调用的控件的信息,因为这会破坏这一点。

那么你觉得……这会有用吗?……有没有更好的方法来实现这一点?有没有办法实现这一目标?

4

7 回答 7

1

我认为,虽然表面上有用,但这种事情最好由以下两者之一来处理:

  • Duck Typing 通过动态(在 VS2010 中出现)提供了更大的灵活性,尽管消除了类型安全性。
  • 通用组成

您的原始示例都不需要,并且可以轻松地像这样工作:

public interface IControlProvider<T>
    where T : Control, ISpecialControl
{
    T CreateControl();
}

public class SpecialTextBoxProvider : IControlProvider<SpecialTextBox>
{
    public SpecialTextBox CreateControl()
    {
        return new SpecialTextBox();
    }
}

事实上,鉴于大多数控件都有一个默认的公共构造函数,你可以让它变得更容易:

public class ControlProvider : IControlProvider<T>
    where T : Control, ISpecialControl, new()
{
    public T CreateControl()
    {
        return new T();
    }
}

var control = ControlProvider<SpecialTextBox>.CreateControl();

鉴于调用代码的额外限制不直接对 SpecialTextBox 进行引用依赖,这将不起作用。

于 2009-02-16T22:32:40.260 回答
0

这与扩展方法有很大不同吗?为什么不只是扩展控件?

于 2009-05-19T04:28:10.443 回答
0

使用接口组合怎么样?

你有

interface IA;
class B;

如果存在,则使用 B 类的接口并制作复合材料

interface IB;
interface IAB : IA, IB;

修改类

class C : B, IA

实现复合接口(不应该出现问题)

class C : B, IAB

您可以使用接口 IAB 作为约束类型。

于 2009-02-16T12:43:00.643 回答
0

我不明白你在提议什么,我认为这是我的问题。

但是,在我看来,您想返回一个实现特定接口的控件。

为什么不能只说该方法返回该特定接口?当您在说“我想返回从 Control 继承的类,并且还实现 ISomeInterface 接口”时,这听起来对我来说。

也许解决方案是将您需要的 Control 部分提取到您自己的接口中,并指定您要返回实现这两者的对象?

像这样:

interface IControl { ... }
interface ISomeInterface { ... }

interface IControlInterface : IControl, ISomeInterface { ... }

最后一个接口将指定您需要一个实现两个基接口的对象,这听起来像您想要的,只是您希望其中一个要求是基类,而不是接口。

我认为你可以通过使用接口来解决这个问题。

于 2009-02-16T12:51:52.870 回答
0

编辑:哦,等等,我没有正确阅读,这是关于将其限制为 2 种类型,而不是选择 2 种类型。我认为你可以解决它类似于下面提到的课程......

我曾经尝试过一个类来模拟这种行为:

public class Choice<T1, T2> 
{
    private readonly T1 value1;
    private readonly T2 value2;

    public Choice(T1 value)
    {
        value1 = value;
    }

    public Choice(T2 value) 
    {
        value2 = value;
    }

    public static implicit operator T1(Choice<T1, T2> choice) 
    {
        return choice.value1;   
    }

    public static implicit operator T2(Choice<T1, T2> choice) 
    {
        return choice.value2;   
    }

    public static implicit operator Choice<T1, T2>(T1 choice) 
    {
        return new Choice<T1, T2>(choice);
    }

    public static implicit operator Choice<T1, T2>(T2 choice) 
    {
        return new Choice<T1, T2>(choice);
    }

    public T1 Value1
    {
        get { return value1; }
    }

    public T2 Value2
    {
        get { return value2; }
    }
}

它允许您创建这样的结构:

[TestMethod]
public void TestChoice() 
{
    TestChoice("Hello");
    TestChoice(new []{"Hello","World"});
}

private static void TestChoice(Choice<string[], string> choice)
{
    string value = choice;
    string[] values = choice;

    if (value != null)
        Console.WriteLine("Single value passed in: " + value);

    if (values != null) {
        Console.WriteLine("Multiple values passed in ");
        foreach (string s in values)
            Console.WriteLine(s + ",");
    }
}

隐式运算符的缺点是它不会对接口进行隐式转换(它确实可以从接口类型转换),因此在尝试将包含的值从to 转换回时,传入 anIEnumerable<T>而不是 astring[]将不起作用。Choice<IEnumerable<T>, T>IEnumerable<T>

一般来说,我宁愿只使用重载/接口顺便说一句,所以在这里不要误会我的意思:)

在您使用属性(并且因此不能使用重载)的情况下,它会更有用,例如仅允许list<string>list<ListItem>例如的 DataSource 属性。

于 2009-05-20T15:29:40.457 回答
0

它比“受约束的类型”更“受约束的变量” - 但绝对有趣。与通用约束一样,如果存在冲突的成员,可能会有一些解决问题的小问题,但同样的解决方法可能适用于通用约束。

当然,务实的观点是你已经可以投射等,但是你需要保持这两个参考相互更新......

嗯……有趣。

于 2009-02-16T11:51:56.383 回答
0

这似乎是一个有用的语言功能。除了遵守多个约束之外,可能有类型未知的方法参数,但是没有办法将这样的参数存储在字段中,以便它可以传递给这样的函数,甚至类型转换为通过了。

然而,还有另一种方法可以实现您所追求的基本结果,尽管它有点笨拙。请参阅我的问题存储实现多个接口并从某个基础(.net)派生的对象,其中提供了我能够制定的最佳方法。

于 2010-12-13T23:45:16.307 回答