【问题标题】:C# - How can I have an type that references any object which implements a set of Interfaces?C# - 我怎样才能拥有一个引用任何实现一组接口的对象的类型?
【发布时间】:2011-11-13 22:18:06
【问题描述】:

我怎样才能有一个引用任何实现一组接口的对象的类型引用?

例如,我可以有一个这样的泛型:

Java:

public class Foo<T extends A & B> { }

C#

public class Foo<T> where T : A, B { }

这就是如何拥有一个类范围的泛型类型。但是,我只想拥有一个引用任何扩展给定接口集的对象的数据成员。

例子:

public class Foo
{
    protected <? extends A, B> object;

    public void setObject(<? extends A, B> object)
    {
        this.object = object;
    }
}

如果有可能有这种类型的语法,我怎么能在 Java 和 C# 中做到这一点?


我意识到我可以创建另一个接口来扩展所有需要的接口。但是,我不认为这是最优的,因为它不必要地添加了另一种类型,其唯一目的是绕过语法。诚然,这是一个很小的问题,但就优雅而言,它有点令人讨厌。

【问题讨论】:

  • 目前正在苦苦挣扎。 Foo.setObject 以何种方式向 A & B 添加更多行为?
  • 我举了一个抽象的例子。这是一个更具体的例子:我从事游戏编程,我的主游戏循环有两个主要功能:更新和渲染。在那里,我将调用委托给“gameworld”对象,该对象有自己的渲染和更新方法。只是,我不想强​​迫我的游戏世界只是特定类的实例。我更愿意将所需的功能拆分为一组接口(在本例中为可更新和可渲染),并且能够使用任何对象来实现所有这些接口。这是一个小问题,但我认为它在优雅方面很重要。
  • 为什么不把私有变量声明为A,或者B

标签: c# java syntax interface


【解决方案1】:

由于 2 年不活动,我的 Java 有点生疏了。

这是我的 C# 方法:(请参阅 https://ideone.com/N20xU 以获取完整的工作示例)

public class Foo
{
    private IInterface1 _object; // just pick one

    public void setObject<T>(T obj)
        where T : IInterface1, IComparable<T>, IEtcetera
    {
        // you now *know* that object supports all the interfaces
        // you don't need the compiler to remind you
        _object = obj; 
    }

    public void ExerciseObject()
    { 
        // completely safe due to the constraints on setObject<T>
        IEtcetera itf = (IEtcetera) _object;

        // ....
    }

【讨论】:

  • 虽然不是最佳选择,但我想不出更好的了。
  • 你试过这个解决方案了吗?我试了好几个版本都没有成功...
  • @kol:当然。我的答案是 sn-p(以防丢失的端括号没有放弃!)。这是一个完整的示例:ideone.com/N20xU(该站点今天很慢)。 HTH @majocha:我明白你的意思。我将_object 设为私有。在信任编译器和信任您的测试覆盖率之间寻求平衡。这两个选项都可以很好地工作。
  • 这是一个很好的尝试,但它很乱。 (我特别不喜欢在我想使用其他接口时强制转换对象。)如果这是我唯一的其他选择,我想我更愿意简单地创建一个新接口。不过,为尝试 +1。
  • @CodeBunny:如果你不喜欢转换它,你可以让访问器返回适当的接口。否则,只需咬紧牙关并已经定义该自定义界面:)
【解决方案2】:

据我所知,你不能创建一个带有约束的变量,你只能创建一个给定类型的变量。该类型具有约束。这意味着您必须定义具有所需约束的类型,然后使用该类型创建变量。

这对我来说似乎是合乎逻辑的,我不明白为什么你觉得必须为你需要的东西定义一个类型是“令人讨厌的”。

【讨论】:

  • 这并不难,因为它很难;这很烦人,因为这对我来说似乎没有必要。需要创建一个单独的类型来简单地允许一个类型使用多个“低级”接口似乎是一个糟糕的举动。设计的界面使其具有非常灵活的设计,我的问题与我认为的逻辑扩展一致。
  • 另外,我认为只有在实际添加功能时才应该创建新类型。仅出于混合界面的目的而创建新类型似乎是落后的。
  • @CodeBunny - 它与委托没有什么不同,您可以在委托中创建函数签名类型以用作调用方法的接口。
【解决方案3】:

在 C# 中,您可以使用元组将值存储在 superposition 中:

public class Foo {
  private Tuple<IA, IB> junction;
  public void SetValue<T>(T value) where T : IA, IB {
    junction = Tuple.Create<IA, IB>(value, value);
  }
}

您还可以有一个专门的类来强制两个值引用同一个对象的约束:

public class Junction {
  public IA A { get; private set; }
  public IB B { get; private set; }
  private Junction() { }
  public static Junction Create<T>(T value) where T: IA, IB {
    return new Junction {
      A = value,
      B = value
    };
  }
}

public class Foo {
  private Junction junction;
  public void SetValue<T>(T value) where T : IA, IB {
    junction = Junction.Create(value);
  }
}

在 Java 中,通配符会简化一些事情

class Junction<E extends A & B> {
  private final E value;
  public Junction(E value) {
    this.value = value;
  }
  public E getValue() {
    return value;
  }
}

class Foo {
  private Junction<?> junction;
  public <E extends A & B> void setValue(E value) {
    junction = new Junction<E>(value);
  }
}

或者您可以将 aliases 设置为相同的值(C#,但也适用于 Java):

public class Foo {
  private IA a;
  private IB b;
  public void SetValue<T>(T value) where T : IA, IB {
    a = value;
    b = value;
  }
}

【讨论】:

  • 这是最接近我要求的。我不知道这是否是最好的整体解决方案,因为它需要一些额外的包袱,但它实际上是我想要的。 :)
  • @CodeBunny:很高兴我能帮上忙 :)
【解决方案4】:

我认为简单地将约束声明为私有接口没有任何问题:

class Foo
{
    interface R : I1, I2 { }
    R _object;

    void setObject(R r) { _object = r; }
}

【讨论】:

  • 添加接口的主要问题是你需要修改你想要接受的每个类型来实现这个接口。根据定义这些类型的位置,这可能很丑陋或不可能。当然,接口至少需要与SetObject 方法一样可访问。 【这里说的是C#,不懂java】
【解决方案5】:

这是我能想到的最好的解决方案(但仍然不是最佳解决方案)

public class Foo
{
    private TypeWrapper<IInterface1,IInterface2> _object;
    public void setObject<T>(T obj)
        where T : IInterface1, IInterface2
    {
        _object = new TypeWrapper<IInterface1, IInterface2>();
        _object.SetObject(obj);

        var val = _object.Get(h => h.c);
        Console.WriteLine(val);
        _object.Do(h => h.c = 25);
        val = _object.Get(h => h.c);
        Console.WriteLine(val);
        _object.Do(h => h.someF());
    }
}

public class TypeWrapper<TType, TTypeTwo>
{
    private Object m_Obj;
    public void SetObject<T>(T obj) where T : TType, TTypeTwo
    {
        m_Obj = obj;
    }

    public T Get<T>(Func<TType, T> action)
    {
        return (T)action((TType)m_Obj);
    }
    public T Get<T>(Func<TTypeTwo, T> action)
    {
        return (T)action((TTypeTwo)m_Obj);
    }

    public void Do(Action<TTypeTwo> action)
    {
        action((TTypeTwo)m_Obj);
    }

    public void Do(Action<TType> action)
    {
        action((TType)m_Obj);
    }
}

public class myClass : IInterface1, IInterface2 {
    public int t {get;set;}
    public int c {get;set;}
    public void someF() { Console.WriteLine("Fired"); }
    }
public interface IInterface1 {
    int t { get;set;} 
    void someF();
}
public interface IInterface2 { 
    int c { get;set; }
}

您将不得不通过 Get 和 Do 方法处理对象,但如果接口发生更改,它将与智能感知一起工作并引发编译时错误。

【讨论】:

  • 不幸的是,将 m_Obj 传递给期望类型受限于 IInterface1 和 IInterface2 的泛型方法的唯一方法是在 Foo 中包含直接调用泛型方法(而不是委托)的代码,或者让 Foo 的构造函数接受 m_Obj 应该传递到的所有此类方法的委托,并将此类委托存储在 Foo 中。这两种方法似乎都没有吸引力。
猜你喜欢
  • 2015-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-07
  • 2017-06-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多