返回接口泛型类的Yield

时间:2018-04-16 10:44:15

标签: c# csv

我正在努力解决如何返回类型为IBasic的接口类型的Yield。目前我可以有三种不同类型的IBasic:InputData1,InputData2,InputData3。

问题在于这部分代码:

 internal class CsvRepo<T> : ICsvRepo<T> where T : IBasic
    {
        private readonly ICsvSettings _settings;
        public CsvRepo(ICsvSettings settings)
        {
            _settings = settings;
        }

        public IEnumerable<T> GetRecords()
        {
            //return from line in File.ReadLines(_settings.Path)
            //    select line.Split(',') into parts
            //    where parts.Length == 3
            //    select new InputData { X = Convert.ToInt32(parts[1]), Y = Convert.ToInt32(parts[2]) };
        }
    }

行:select new InputData

我会说return new IBasic之类的东西,但不同的InputDataX有不同的参数,我不知道该怎么做?有可能吗?

这是完整代码:

namespace ClassLibrary3
{
    public interface IRepo { }
    public interface IRepository<T> : IRepo where T : IBasic { }

    public interface ICsvRepo<T> : IRepository<T> where T : IBasic
    {
        IEnumerable<T> GetRecords();
    }

    public interface ISqlRepo
    {
    }

    public interface IOracleRepo<T> : IRepository<T> where T : IBasic { }

    public interface IRepoX : IRepo { }

    public interface ICsvSettings
    {
        string Path { get; }
        string FileName { get; }
    }

    public interface ISqlSettings
    {
        string ConnectionString { get; }
        string Username { get; }
        string Password { get; }
    }

    internal class CsvSettings : ICsvSettings
    {
        public string Path { get; set; }
        public string FileName { get; set; }
    }

    internal class SqlSettings : ISqlSettings
    {
        public string ConnectionString { get; set; }
        public string Username { get; set; }
        public string Password { get; set; }
    }


    internal class CsvRepo<T> : ICsvRepo<T> where T : IBasic
    {
        private readonly ICsvSettings _settings;
        public CsvRepo(ICsvSettings settings)
        {
            _settings = settings;
        }

        public IEnumerable<T> GetRecords()
        {
            return null;
            //return from line in File.ReadLines(_settings.Path)
            //    select line.Split(',') into parts
            //    where parts.Length == 3
            //    select new InputData { X = Convert.ToInt32(parts[1]), Y = Convert.ToInt32(parts[2]) };
        }
    }

    internal class SqlRepo : ISqlRepo
    {
        private readonly ISqlSettings _settings;
        private readonly IRepoX _repoX;

        public SqlRepo(ISqlSettings settings, IRepoX repoX)
        {
            _settings = settings;
            _repoX = repoX;
        }
    }

    internal class OracleRepo<T> : IOracleRepo<T> where T : IBasic
    {
        private readonly ISqlSettings _settings;
        private readonly IRepoX _repoX;

        public OracleRepo(ISqlSettings settings, IRepoX repoX)
        {
            _settings = settings;
            _repoX = repoX;
        }
    }

    internal class OracleRepo333<T> : IOracleRepo<T> where T : IBasic
    {
        private readonly ISqlSettings _settings;
        private readonly IRepoX _repoX;

        public int id;

        public OracleRepo333(ISqlSettings settings, IRepoX repoX)
        {
            _settings = settings;
            _repoX = repoX;
        }
    }

    internal class RepoX : IRepoX { }

    public class RepoModule : NinjectModule
    {
        private readonly string _username;
        private readonly string _password;
        public RepoModule(string username, string password)
        {
            _username = username;
            _password = password;
        }
        public override void Load()
        {
            Bind<ICsvSettings>().ToConstant(new CsvSettings
            {
                FileName = "foo",
                Path = Config.Instance.ServerName,
            }).InSingletonScope();

            Bind<ISqlSettings>().ToConstant(new SqlSettings
            {
                ConnectionString = "foo",
                Password = _password,
                Username = _username
            }).InSingletonScope();



            Bind<IRepoX>().To<RepoX>();

            Bind(typeof(ICsvRepo<>)).To(typeof(CsvRepo<>));
            Bind(typeof(ISqlRepo)).To(typeof(SqlRepo));

            Bind(typeof(IOracleRepo<>)).To(typeof(OracleRepo<>));
            Bind(typeof(IOracleRepo<>)).To(typeof(OracleRepo333<>));
        }
    }

    public interface IBasic
    {
    }

    public class InputData1 : IBasic
    {
        public int X;
        public int Y;
    }

    public class InputData2 : IBasic
    {
        public string Name;
    }

    public class InputData3 : IBasic
    {
        public IEnumerable<string> WhateverList;
    }

}

  class Program
    {
        static void Main(string[] args)
        {
            var kernel = new StandardKernel(new RepoModule("foo", "bar")); /*some other modules here maybe?*/

            //thousand of code lines later...

            var csvRepo = kernel.Get<ICsvRepo<InputData1>>();
            //var data = FetchData(csvRepo);

            var sqlRepo = kernel.Get<ISqlRepo>();
            //data = FetchData(sqlRepo);

           // var oracleRepo = kernel.Get<IOracleRepo<InputData>>();
            //data = FetchData(oracleRepo);

            var oracleRepos = kernel.GetAll<List<IOracleRepo<InputData1>>>();}
            }

        }

        //static T[] FetchData<T>(IRepository<InputData> repo)
        //{
        //    throw new NotImplementedException();
        //}
    }

2 个答案:

答案 0 :(得分:2)

问题是您正在尝试返回期望泛型类型的具体类型。请考虑以下CsvRepo<T>

的实例化
var repo = new CsvRepo<InputData1Derived>(null);
repo.GetRecords().First().PropFromInputData1Derived

当调用者期望InputData时,您正在实例化InputDataDerived。这就是编译器不允许你这样做的原因。

你可以有几个解决方案,让CsvRepo可以是抽象的,并为特定的类实现它:

internal abstract class CsvRepo<T> : ICsvRepo<T> where T : IBasic
{
    public CsvRepo()
    {
    }

    public abstract IEnumerable<T> GetRecords();
}

internal class InputDataCsvRepo : CsvRepo<InputData1>
{
    public override IEnumerable<InputData1> GetRecords()
    {
        return from line in File.ReadLines(_settings.Path)
               select line.Split(',') into parts
               where parts.Length == 3
               select new InputData { X = Convert.ToInt32(parts[1]), Y = Convert.ToInt32(parts[2]) };

    }
}

或者您可以使T参数具有默认构造函数并使用它(但IBasic中只有属性可以初始化,这可能不是您想要的。

答案 1 :(得分:0)

这似乎是if you have a hammer everything looks like a nail的情况之一。简而言之,没有必要为方法设置泛型的参数,因为已经知道任何实现的具体返回类型,并且您希望该返回类型实现特定的接口。

删除方法T上的泛型类型约束GetRecords,并将其替换为IBasic接口约束。

public interface ICsvRepo<T> : IRepository<T> where T : IBasic
{
    IEnumerable<IBasic> GetRecords();
}

internal class CsvRepo<T> : ICsvRepo<T> where T : IBasic
{
    private readonly ICsvSettings _settings;
    public CsvRepo(ICsvSettings settings)
    {
        _settings = settings;
    }

    public IEnumerable<IBasic> GetRecords()
    {
        return from line in File.ReadLines(_settings.Path)
            select line.Split(',') into parts
            where parts.Length == 3
            select new InputData { X = Convert.ToInt32(parts[1]), Y = Convert.ToInt32(parts[2]) };
    }
}

我将下面的代码更改为仅包含显示解决方案所需的内容。

在一个不相关的说明中,公开字段几乎总是一个坏主意,因为你暴露了类的内部。使用公共getter / setter将它们变为属性。

示例:

public class InputData : IBasic
{
    public int X {get;set;}
    public int Y {get;set;}
}