问题
How can I have a type reference that refers to any object that implements a set of interfaces?
For example, I can have a generic type like this:
Java:
public class Foo<T extends A & B> { }
C#
public class Foo<T> where T : A, B { }
That's how to have a class-wide generic type. However, I'd like to simply have a data member which references any object that extends a given set of interfaces.
Example:
public class Foo
{
protected <? extends A, B> object;
public void setObject(<? extends A, B> object)
{
this.object = object;
}
}
If it's possible to have this sort of type syntax, how could I do it in both Java and C#?
I realize I can just create another interface that extends all desired interfaces. However, I don't see this as optimal, as it needlessly adds another type whose sole purpose is to get around syntax. Granted this is a very minor issue, but in terms of elegance it's a bit galling.
回答1:
In C#, you can use a tuple to store the value in a kind of 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);
}
}
You can also have a specialized class to enforce the constraint that both values reference the same object:
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);
}
}
In Java, a wildcard would simplify things a little:
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);
}
}
Or you can have aliases to the same value (C#, but also applicable to Java):
public class Foo {
private IA a;
private IB b;
public void SetValue<T>(T value) where T : IA, IB {
a = value;
b = value;
}
}
回答2:
My Java has become a bit rusty through 2years of inactivity.
Here's my C# approach: (see https://ideone.com/N20xU for full working sample)
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;
// ....
}
回答3:
As far as I know, You cannot create a variable with constraints on it, you can only create a variable of a given type. The type has the constraints. This means you have to define a type that has the constraints you desire, then create the variable with that type.
This seems logical to me, and I don't see why you find it "galling" to have to define a type for what you need.
回答4:
I don't see any problem with simply stating the constraints as a private interface:
class Foo
{
interface R : I1, I2 { }
R _object;
void setObject(R r) { _object = r; }
}
回答5:
Here's the best I could come up with (but still not an optimal solution)
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; }
}
You're going to have to work on the object through the Get and Do methods, but it'll work with intellisense and throw compile time errors if the interfaces change.
来源:https://stackoverflow.com/questions/8115191/c-sharp-how-can-i-have-an-type-that-references-any-object-which-implements-a-s