定义实现泛型的接口依赖性

时间:2012-08-22 14:40:58

标签: c# .net generics

对于很多人来说这将是泛型101,但下面是示例代码,所以我可以更好地理解。

public interface IRecordedItemsProcessor<T>
{
    ObservableCollection<RecordedItem> Load(string name);
    void Save();
    RecordedItem Parse(T itemToParse);
}

public class FileLoadingProcessor : IRecordedItemsProcessor<string>
{
    public ObservableCollection<RecordedItem> Load(string name)
    {
    }

    public void Save()
    {
    }

    public RecordedItem Parse(string itemToParse)
    {
    }
}

public class MyClass
{
    public MyClass(IRecordedItemsProcessor<T> processor)
    {

    }

}

问题是MyClass需要依赖IRecordedItemsProcessor<T>但不会编译,因为它不知道T是什么。怎么解决这个问题?使MyClass实现似乎很奇怪,因为它需要做的就是调用Load / Save

由于

4 个答案:

答案 0 :(得分:5)

第一个解决方案是最简单的解决方案:将通用声明提升到类级别,例如

public class MyClass<T>
{
    public MyClass(IRecordedItemsProcessor<T> processor)
    {

    }
}

然后您可以将MyClass实例化如下:

var myClass = new MyClass<string>(new FileLoadingProcessor());
Console.WriteLine (myClass);

第二个解决方案是从构造函数和推断类型中删除泛型输入。然后,您不需要完全从调用中指定泛型。类声明将如下所示:

public class MyClass
{
    public void Process<T>(IRecordedItemsProcessor<T> processor)
    {

    }
}

然后你可以简单地打电话

var my = new MyClass();
my.Process(new FileLoadingProcessor());

理念是你总是需要明确指定类级泛型,但是编译器可以推断出方法级泛型。

第三种解决方案是将创建机制封装在MyClassFactory中。这是非常灵活的,但它看起来有点复杂,因为IRecordedItemsProcessor<T>的后代不在类级别定义泛型,所以我们应该去实现接口并抓住泛型类型。只有这样我们才能构建Generic MyClass。清单如下:

public class MyClassFactory
{
    public MyClass<T> MakeMyClassFor<T>(IRecordedItemsProcessor<T> processor)
    {
        var processorGenericType = processor.GetType()
                                            .GetInterfaces()
                                            .Single(intr=>intr.Name == "IRecordedItemsProcessor`1")
                                            .GetGenericArguments()[0];
        var myClassType = typeof(MyClass<>).MakeGenericType(processorGenericType);
        return Activator.CreateInstance(myClassType, processor) as MyClass<T>;
    }
}

现在您可以非常简单地创建MyClass

 var myClassFactory = new MyClassFactory();
 var res = myClassFactory.MakeMyClassFor(new FileLoadingProcessor());
 Console.WriteLine (res);

所有这三种方法都有其优点和缺点。考虑考虑您将要使用它们的上下文。

答案 1 :(得分:0)

您可以执行以下操作:

  1. 创建新界面IRecordedItemsProcessor(非通用)
  2. LoadSave移至此IRecordedItemsProcessor
  3. IRecordedItemsProcessor<T>继承此IRecordedItemsProcessor
  4. 在其构造函数中MyClass期待IRecordedItemsProcessor
  5. 这清楚地表明MyClass并不关心处理器可以解析什么类型,甚至根本不能解析事物 - 它只知道它可以保存和加载。

答案 2 :(得分:0)

正在编写的泛型类型在MyClass构造函数上声明,这意味着必须在MyClass级别定义泛型类型:

public class MyClass<T>
{
    public MyClass(IRecordedItemsProcessor<T> processor)
    {

    }
}

但是,如果泛型类型是在方法级别声明的,则只需在方法级别定义:

public class MyClass
{
    public void MyMethod<T>( IRecordedItemsProcessor<T> processor )
    {

    }
}

修改
根据您的评论:

  

我想要一个可以调用Load / Save方法的类,但不要担心   T是。

然后你需要2个接口:1个用于加载/保存,然后一个用于解析。在这种情况下,您可以使用继承:

public interface IRecordedItems
{
    ObservableCollection<RecordedItem> Load( string name );
    void Save();
}


public interface IRecordedItemsProcessor<T> : IRecordedItems
{
    RecordedItem Parse( T itemToParse );
}

public class MyClass : IRecordedItems
{
    #region Implementation of IRecordedItems

    public ObservableCollection<RecordedItem> Load( string name )
    {
        throw new NotImplementedException();
    }

    public void Save()
    {
        throw new NotImplementedException();
    }

    #endregion
}

编辑2
根据您的gist example,类型依赖关系可以从界面移出并直接移到界面方法中:

public class RecordedItem {}

public interface IRecordedItemsProcessor
{
    ObservableCollection<RecordedItem> Load( string name );

    void Save();

    RecordedItem Parse<T>( T itemToParse );
}

public class MyClass
{
    private readonly IRecordedItemsProcessor _processor;

    public MyClass( IRecordedItemsProcessor processor )
    {
        _processor = processor;

        processor.Parse<string>( "foo" );
        processor.Parse<int>( 10 );
        processor.Parse<RecordedItem>( new RecordedItem() );
    }
}

答案 3 :(得分:0)

您可以从非通用标记接口继承,这样就无需了解类中的T:

public interface IRecordedItemsProcessor 
{
}

public interface IRecordedItemsProcessor<T> : IRecordedItemsProcessor
{
    ObservableCollection<RecordedItem> Load(string name);
    void Save();
    RecordedItem Parse(T itemToParse);
}

然后您可以使用任何IRecordedItemsProcessor,如:

public class MyClass
{
    public MyClass(IRecordedItemsProcessor processor)
    {

    }

}