我正在努力解决如何返回类型为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();
//}
}
答案 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;}
}