【问题标题】:Is it possible to make an object expose the interface of an type parameter?是否可以让对象公开类型参数的接口?
【发布时间】:2013-01-09 07:45:25
【问题描述】:

在C#中,是否可以这样写:

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

我知道上面的实现无法编译,但我实际上想要实现的是对未知类型实现某种通用包装器,以便客户端可以像调用类型一样调用包装器,前提是通过参数T,而不是使用类似wrapper.Instance.SomeMember() 的方式调用它。

提前致谢!

【问题讨论】:

  • 所以如果你有class Foo { public string A; },你希望能够做类似MyClass&lt;Foo&gt;.A的事情吗?
  • 这正是我要找的 :)
  • 我明白 :) 出于好奇,您有什么理由愿意这样做而不是 MyClass&lt;Foo&gt;.Instance.A
  • Monads 不能满足您的需求吗? mikehadlow.blogspot.fr/2011/01/…
  • @LukeHennerley:我想让这个类来管理T类型的对象的实例,这样客户端就不需要关心何时需要创建实例。在非托管世界中,您会谈论智能指针之类的东西。我认为在 C++ 中,我需要的行为可以通过重载 this-&gt; 运算符来实现,返回包含的实例。

标签: c# oop design-patterns generics


【解决方案1】:

这是不可能的。

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

例如,假设我们有一个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&lt;T&gt; 和基本的通用约束是如何完成这项工作的;)

总结:

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

我相信这个示例向您展示了如何获得所需的内容并“按原样”使用 C#!

【讨论】:

  • 没错,因为CarEngine之间的关系不像a Car is an Eingine,更像是a Car contains an Engine 并且您确实需要知道哪个引擎。这就是为什么我要求“类似”的原因。我不是在寻找基于继承的解决方案,我只是在询问是否有可能,如果有,如何。 ;)
  • 在某些情况下,类获取对象并将其包装以暴露大部分底层行为,但更改一些细微方面是有用的。例如,可能希望将Stream 传递给StreamReader,但在StreamReader 完成后让它仍然有用。目前,这样做需要使用样板代码将包装器的几乎所有成员转发到被包装对象的相应成员,或者可能使用诸如 Castle Dynamic Proxy 之类的东西;语言支持似乎更优雅(但还不存在)。
  • @supercat 你是对的。但我更喜欢给出没有反射发射的“官方版本”,因为我认为 OP 的主要问题更多是关于如何在没有调用者/开发者干预的情况下初始化关联对象。
  • 尽管这并不能真正解决我的问题,但答案回答了我的问题,描述了为什么我的问题无法按照我想要的方式解决,并提出了替代方案。谢谢你:-)
  • @Aschratt 不客气!您是否设法使用这种方法解决了您的问题?
【解决方案2】:

您可以查看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();
    }
}

【讨论】:

  • 我不明白为什么动态类型在 OP 的问题中有意义。检查另一个答案是否相同:结果与使用强类型相同。
  • 不同之处在于您不需要实现所有包装的属性(和方法),而是使用 TrySet/Get 方法。当然,潜在的问题可以通过使用动态来解决;我只是试图回答有关包装的问题。
  • 我猜您的意思是“使用动态可能无法解决潜在问题”:D 我理解您的观点。
  • 是的。当出现这样的问题时,我总是怀疑设计。但是为了让这个页面对其他人有用,如果问题是可以回答的,那么应该有一个答案。
  • 我真的相信您的解决方案只是隐藏了一个糟糕的设计决策。或者更糟糕的是:这是懒惰的解决方案! :D “因为我想访问成员,就好像它们是类的一部分一样,所以我使对象动态化”。我想相信动态对象是为了其他事情,而不是在访问整个成员时避免使用点和标识符哈哈。但是,是的,也许不是为了这个问题,而是为了其他任何事情,它很好地证明了动态对象的强大:)
猜你喜欢
  • 2011-03-11
  • 2017-08-07
  • 2010-10-20
  • 1970-01-01
  • 2015-03-17
  • 1970-01-01
  • 1970-01-01
  • 2010-09-07
相关资源
最近更新 更多