简体   繁体   English

C#中的泛型类

[英]Generic Class in C#

Let's say I have the following: 假设我有以下几点:

public class DataType1
{
    public DataType1() { }

    public string Read() { return "dt1"; }
}

public class DataType2
{
    public DataType2() { }

    public string Read() { return "dt2"; }
}

And a utility class: 和一个实用程序类:

public class Logger<T>
{
    public Logger() { }
}

Then, in my main app: 然后,在我的主应用程序中:

class Program
{
    static void Main(string[] args)
    {
        var test1 = new Logger<DataType1>();
        test1.Read(); // I want this to equal "dt1";

        var test2 = new Logger<DataType2>();
        test2.Read(); // I want this to equal "dt2";
    }
 }

I realize that I'm trying to do a type of covariance. 我意识到我正在尝试做一种协方差。 However, I cannot have DataType1/DataType2 inherit from Logger - that would be against architecture (ie. DataType1/DataType2 are at the DAL, and the programmers are required to go through the Logger to get to the DAL). 但是,我不能从Logger 继承 DataType1 / DataType2-这将与体系结构背道而驰(即DataType1 / DataType2位于DAL,并且程序员必须通过Logger才能到达DAL)。

Any thoughts on how I could accomplish this? 关于如何实现此目标有任何想法吗?

Thanks. 谢谢。

Use an interface: 使用界面:

public interface IDataType
{
    string Read();
}

public class DataType1 : IDataType
{
    public DataType1() { }

    public string Read() { return "dt1"; }
}

public class DataType2 : IDataType
{
    public DataType2() { }

    public string Read() { return "dt2"; }
}

public class Logger<T> where T : IDataType, new()
{
    IDataType dataType { get; set; }

    public Logger() {
        dataType = new T();
    }

    public string Read()
    {
        return dataType.Read();
    }
}

class Program
{
    static void Main(string[] args)
    {
        var test1 = new Logger<DataType1>();
        test1.Read(); // I want this to equal "dt1";

        var test2 = new Logger<DataType2>();
        test2.Read(); // I want this to equal "dt2";
    }
}

Btw, I think a better practice would be to drop the generics: 顺便说一句,我认为更好的做法是删除泛型:

public interface IDataType
{
    string Read();
}

public class DataType1 : IDataType
{
    public DataType1() { }

    public string Read() { return "dt1"; }
}

public class DataType2 : IDataType
{
    public DataType2() { }

    public string Read() { return "dt2"; }
}

public class Logger
{
    IDataType dataType { get; set; }

    public Logger(IDataType dt) {
        dataType = dt;
    }
    public string Read()
    {
        return dataType.Read();
    }
}

class Program
{
    static void Main(string[] args)
    {
        var dt1 = new DataType1();
        var test1 = new Logger(dt1);
        test1.Read(); // I want this to equal "dt1";

        var dt2 = new DataType2();
        var test2 = new Logger(dt2);
        test2.Read(); // I want this to equal "dt2";
    }
}

Use interfaces: 使用界面:

public class DataType1 : IReadable
{
    public DataType1() { }

    public string Read() { return "dt1"; }
}

public class DataType2 : IReadable
{
    public DataType2() { }

    public string Read() { return "dt2"; }
}

public interface IReadable
{
    string Read();
}

And enforce that the generic type implements this interface: 并强制泛型类型实现此接口:

public class Logger<T> where T : IReadable

You'll have to get a reference to the instance which performs the Read operation, and then you can actually have a delegating Read action on the logger itself: 您必须获得对执行Read操作的实例的引用,然后您实际上可以对记录器本身进行委派的Read操作:

public class Logger<T> where T : IReadable
{
    private readonly T _readable;

    public Logger<T>(T readable)
    {
        this._readable = readable;
    }

    public string Read()
    {
        return this._readable.Read();
    }
}

And usage would be: 用法将是:

var dt1 = new DataType1();
var dt2 = new DataType2();
var logger1 = new Logger<DataType1>(dt1);
var logger2 = new Logger(dt2); // can omit generic noise!
Console.WriteLine(logger1.Read()); // "dt1"
Console.WriteLine(logger2.Read()); // "dt2"

If you want to avoid having to create the instance and pass it to the logger, you can instantiate it inside the logger, but you'll have to add the new() constraint, which means that there is a public constructor that takes no parameters, and you can even have the logger implement IReadable itself: 如果要避免创建实例并将其传递给记录器,则可以在记录器中实例化它,但是必须添加new()约束,这意味着有一个不带参数的公共构造函数,甚至可以让记录器本身实现IReadable

public class Logger<T> : IReadable where T : IReadable, new()
{
    private readonly T _readable;

    public Logger<T>()
    {
        this._readable = new T();
    }

    public string Read()
    {
        return this._readable.Read();
    }
}

And usage would be: 用法将是:

var logger1 = new Logger<DataType1>();
Console.WriteLine(logger1.Read()); // "dt1"

As @leppie said if you would add a constraint on the generic utility class declaration. 正如@leppie所说,是否要在通用实用程序类声明上添加约束。 It could solve the issue. 它可以解决问题。 You would need to inject dependency on your Logger class through the constructor or the use of properties. 您将需要通过构造函数或属性的使用来注入对Logger类的依赖。

Let's say for example you have the following interface: 例如,假设您具有以下界面:

public interface IReader
{
    public String Read();
}

And we would have DataType1 and DataType2 looking like 我们将让DataType1和DataType2看起来像

 public class DataType1:IReader
{
    public String Read()
    {
        return "dt1";
    }
}

Your utility class may turn into something like: 您的实用程序类可能会变成类似以下内容:

 public class Logger<T> where T:IReader
{
    private T dataTypeInstance;

    public Logger(T dataTypeInstance)
    {
        this.dataTypeInstance = dataTypeInstance;
    }

    public String Read()
    {
        return dataTypeInstance.Read();

    }
}

And its Read method would simply invoke the Read method of your DataType class. 而且其Read方法将仅调用DataType类的Read方法。

We can then, get instances of DataType1 and DataType2 from some kind of factory and test with something like: 然后,我们可以从某种工厂获取DataType1和DataType2的实例,并使用类似的方法进行测试:

 var test1 = new Logger<DataType1>(dataType1);
 var test2 = new Logger<DataType2>(dataType2);

 test1.Read(); //will be dt1
 test2.Read(); //will be dt2

Alternatively instead of using dependency injection via a constructor or a property in your utility class you may use reflection and properties files or (database - stored configuration ) along with the is operator to get an instance of the right DataType to use. 另外,也可以使用反射和属性文件或(数据库存储的配置)以及is运算符,而不是通过实用程序类中的构造函数或属性使用依赖项注入,以获取要使用的正确DataType的实例。

If you cant use interface, try using lambda exprestion: 如果您不能使用接口,请尝试使用lambda exprestion:

public class Logger<T>  where T : new()
{
    private Func<T, string> _readFunc;
    private T _member;

    public Logger(Func<T, string> readFunc) 
    {
        _readFunc = readFunc; 
        _member  = new T();
    }

    // Use this if you already have an instance of your data type
    public Logger(Func<T, string> readFunc, T member)
    {
       _readFunc = readFunc;
       _member  = member;
    }

    public string Read()
    {
        return _readFunc(_member); 
    }
}

and then in your app: 然后在您的应用中:

static void Main()
{
    var test1 = new Logger<DataType1>(t => t.Read());
    test1.Read(); // Will be equal "dt1";

    var test2 = new Logger<DataType2>(t => t.Read());
    test2.Read(); // Will be equal "dt2";
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM