是否可以使对象公开类型参数的接口?

时间:2013-01-25 10:47:35

标签: c# oop design-patterns generics

在C#中,是否可以编写如下内容:

public class MyClass<T> : T 
    where T : class, new() 
{
}

我知道上面的实现没有编译,但我实际上试图实现的是将某种通用包装器实现为未知类型,这样客户端就可以调用包装器,就像调用类型一样,提供通过参数T,而不是使用类似wrapper.Instance.SomeMember()的内容来调用它。

提前致谢!

2 个答案:

答案 0 :(得分:4)

这是不可能的。

在我看来,我认为不应该使用继承来实现包装器。

例如,假设我们有一个Engine类,您需要实现FerrariEngine。你有一个Car课程。

您说Car应该继承FerrariEngine。这对我来说太可怕了!

在一天结束时,你正在寻找使用继承来执行依赖注入的事情,而且,这不是正确的道路。

我的建议是不要试图让你的生活更轻松:根据理性点来决定一个架构。

更新

OP在一些评论中表示:

  

我想让这个类来管理类型为T的对象的实例,所以   客户端不需要在实例需要时进行处理   要被创造。

你不需要做出奇怪的事情来得到你想要的东西:

public interface IEngine 
{
     void Start();
}

public sealed class FerrariEngine : IEngine
{
     public FerrariEngine()
     {
          Start();
     }

     public void Start()
     {
     }
}

public abstract class Car<TEngine> where TEngine: IEngine, new()
{
    public Car()
    {
        _engine = new Lazy<TEngine>(() => new TEngine());
    }

    private readonly Lazy<TEngine> _engine;

    public TEngine Engine
    {
        get { return _engine.Value; }
    }
}

public class FerrariCar : Car<FerrariEngine>
{
}

最后,如果我们创建FerrariCar的实例:

Car<FerrariEngine> myFerrari = new FerrariCar();

引擎将被实例化并启动,无需开发人员干预!

检查Lazy<T>和基本通用约束如何完成工作;)

总结:

  • 使用Lazy<T>只有在某些人访问Engine属性时才会实例化引擎。
  • 一旦实例化了延迟加载的引擎,由于FerrariEngine实现了一个无参数的构造函数,它自己调用Start(),它将启动引擎。

我相信这个示例向您展示了如何获得您正在寻找的内容并按原样使用C#!

答案 1 :(得分:1)

您可以查看DynamicObject并执行以下操作:

class Foo<T> : DynamicObject
{
    private T _instance;
    public Foo(T instance)
    {
        _instance = instance;
    }
    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        var member = typeof(T).GetProperty(binder.Name);
        if (_instance != null &&
            member.CanWrite &&
            value.GetType() == member.PropertyType)
        {
            member.SetValue(_instance, value, null);
            return true;
        }
        return false;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        var member = typeof(T).GetProperty(binder.Name);
        if (_instance != null &&
            member.CanRead)
        {
            result = member.GetValue(_instance, null);
            return true;
        }
        result = null;
        return false;
    }
}

class Bar
{
    public int SomeProperty { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var bar = new Bar();
        dynamic thing = new Foo<Bar>(bar);
        thing.SomeProperty = 42;
        Console.WriteLine(thing.SomeProperty);
        Console.ReadLine();
    }
}