英文:
How to handle generic parent child structures in C#?
问题
I'm having a hard time to implement an Api for my Datastructure in C# using generics. Here's what I want to achieve:
static void Main(string[] args) {
Enterprise google = new Enterprise();
new ConverterChain<Enterprise>(google)
.convert(enterprise => enterprise.getWorkers())
.convert(worker => worker.getAddresses())
.doAction(address => Debug.Log(address.getStreet()));
}
The fictional simplified example code does the following:
- find all workers for google and display them on GUI
- wait for the user to select one worker
- find all addresses for the selected worker and display them on GUI
- wait for the user to select one address
- find all streets for the selected address and display them on GUI
- wait for the user to select one street and do some action with that street (i.e. write it to log)
As I want to do some asynchronous stuff I need to store all the Func
s and Action
s in one datastructure for further execution. In order to have a nice Api, I need to use generics. Here's what I have until now:
public class ConverterChain<I> {
I input;
public ConverterChain(I input) {
this.input = input;
}
public Converter<I, O> convert<O>(Func<I, List<O>> c) {
return new Converter<I, O>(c);
}
}
public class Converter<I, O> {
public Func<I, List<O>> c { get; private set; }
public Converter(Func<I, List<O>> c) {
this.c = c;
}
public Converter<O, N> convert<N>(Func<O, List<N>> c) {
return new Converter<O, N>(c);
}
public void doAction(Action<O> action) {
// and now bring somehow all converters into a single structure
}
}
Coming from Java, I would do something like that:
public void doAction(Action<O> action) {
List<Converter<?, ?>> allConverters = getConvertersFromParents();
object rootObject = getInputFromRoot();
GUIHandler.doComplicatedAsynchronousStuff(rootObject, allConverters, action);
}
Without Converter<?, ?>
no matter how I implement those data structures, I don't get the step done to convert all the Lambdas into one data structure. I fail to create the parent-child structure in Converter<I, O>
in a clean generic way as each converter has different I
s and O
s.
Using something like Converter<object, object>
fails as for whatever reason
Converter<I, O> myConverter = ...
Converter<object, object> genericConverter = (Converter<object, object>) myConverter;
is not allowed.
What is the C# way to implement my given scenario?
英文:
I'm having a hard time to implement an Api for my Datastructure in C# using generics. Here's what I want to achieve:
static void Main(string[] args) {
Enterprise google = new Enterprise();
new ConverterChain<Enterprise>(google)
.convert(enterprise => enterprise.getWorkers())
.convert(worker => worker.getAddresses())
.doAction(address => Debug.Log(address.getStreet()));
}
The fictional simplified example code does the following:
- find all workers for google and display them on GUI
- wait for user to select one worker
- find all addresses for the selected worker and display them on GUI
- wait for user to select one address
- find all streets for the selected address and display them on GUI
- wait for user to select one street and do some action with that street (i.e. write it to log)
As I want to do some asyncronous stuff I need to store all the Func
s and Action
s in one datastructure for further execution. In order to have a nice Api I need to use generics. Here's what I have until now:
public class ConverterChain<I> {
I input;
public ConverterChain(I input) {
this.input = input;
}
public Converter<I, O> convert<O>(Func<I, List<O>> c) {
return new Converter<I, O>(c);
}
}
public class Converter<I, O> {
public Func<I, List<O>> c { get; private set; }
public Converter(Func<I, List<O>> c) {
this.c = c;
}
public Converter<O, N> convert<N>(Func<O, List<N>> c) {
return new Converter<O, N>(c);
}
public void doAction(Action<O> action) {
// and now bring somehow all converters into a single structure
}
}
Coming from Java I would do something like that:
public void doAction(Action<O> action) {
List<Converter<?, ?>> allConverters = getConvertersFromParents();
object rootObject = getInputFromRoot();
GUIHandler.doComplicatedAsynchronousStuff(rootObject, allConverters, action);
}
Without Converter<?, ?>
no matter how I implement those data structures, I don't get the step done to convert all the Lambdas into one data structure. I fail to create the parent-child structure in Converter<I, O>
in a clean generic way as each converter has different I
s and O
s.
Using something like Converter<object, object>
fails as for whatever reason
Converter<I, O> myConverter = ...
Converter<object, object> genericConverter = (Converter<object, object>) myConverter;
is not allowed.
What is the C# way to implement my given scenario?
答案1
得分: 1
I think Java uses type erasure to implement generics. It was a long time I coded java, but I think <?>
means "shut off the compile time type checks". In c# the type safety of generic is actually guaranteed by the runtime, so there is no direct equivalence. You could probably extract a list of all the converters if you added a non-generic interface. But you can likely not use Func
, each converter would need to actually reference each other. Such an interface would likely have to use object
to describe input/output.
However, it seems to me like your code is needlessly complicated. If I were to write an asynchronous version of your specifications I would do something like:
public static async Task<Street> GetStreet(
Enterprise enterprise,
Func<Task<T>, T[]> selection)
{
var workers = enterprise.GetWorkers();
var worker = await selection(workers );
var addresses = worker.GetAddresses();
var address = await selection(addresses);
var streets = address.GetStreets();
var street = await selection(streets);
return street;
}
That seems to match your functionality description line for line, more so than your code example. You would, of course, need to write a dialog to select items, but I assume that is out of scope.
英文:
I think Java uses type erasure to implement generics. It was a long time I coded java, but I think <?>
means "shut of the compile time type checks". In c# the type safety of generic is actually guaranteed by the runtime, so there is no direct equivalence. You could probably extract a list of all the converters if you added a non generic interface. But you can likely not use Func
, each converter would need to actually reference each other. Such an interface would likely have to use object
to describe input/output.
However, it seem to me like your code is needlessly complicated. If I where to write an asynchronous version of your specifications I would do something like:
public static async Task<Street> GetStreet(
Enterprise enterprise,
Func<Task<T>, T[]> selection)
{
var workers = enterprise.GetWorkers();
var worker = await selection(workers );
var addresses = worker.GetAddresses();
var address = await selection(addresses);
var streets = address.GetStreets();
var street = await selection(streets);
return street;
}
That seem to match your functionality description line for line, more so than your code example. You would ofc need to write a dialog to select items, but I assume that is out of scope.
答案2
得分: 0
以下是您要翻译的内容:
即使Converter<object, object> genericConverter = (Converter<object, object>) myConverter;
可以编译,但通常情况下它不具备类型安全性(这就是为什么它不能编译的原因,更多信息请查看C#中的协变性与逆变性以及像这个或这个的回答)。
您可以这样做 - 引入非泛型基类,以便将任何转换器放入单个集合中。例如,像这样:
public abstract class Converter
{
public bool CanActOn<T>() => TryGetActor<T>() 不为 null;
protected abstract IActOn<T>? TryGetActor<T>();
public void DoAction<O>(Action<O> action)
{
if (TryGetActor<O>() is { } act)
{
act.doAction(action);
}
else
{
// 抛出/忽略?
}
}
}
public class Converter<I, O> : Converter, IActOn<O>
{
// ....
// 可能要明确实现接口
public void doAction(Action<O> action) {
// 然后以某种方式将所有转换器组合到一个结构中
}
protected override IActOn<T>? TryGetActor<T>() => this as IActOn<T>;
}
public interface IActOn<T>
{
void doAction(Action<T> action);
}
如果目标只是暴露doAction
- 那么您可以只使用IActOn<O>
。
英文:
Even if Converter<object, object> genericConverter = (Converter<object, object>) myConverter;
could compile it would not be type-safe in general case (that is the reason why it does not compile, for more - check out variance in C# and answers like this or this one).
What you can do - introduce non-generic base class so you can put any converter in a single collection. For example like this:
public abstract class Converter
{
public bool CanActOn<T>() => TryGetActor<T>() is not null;
protected abstract IActOn<T>? TryGetActor<T>();
public void DoAction<O>(Action<O> action)
{
if (TryGetActor<O>() is { } act)
{
act.doAction(action);
}
else
{
// throw/ignore ?
}
}
}
public class Converter<I, O> : Converter, IActOn<O>
{
// ....
// maybe make explicit interface implementation
public void doAction(Action<O> action) {
// and now bring somehow all converters into a single structure
}
protected override IActOn<T>? TryGetActor<T>() => this as IActOn<T>;
}
public interface IActOn<T>
{
void doAction(Action<T> action);
}
If the goal is to only have the doAction
exposed - then you can just use IActOn<O>
.
答案3
得分: 0
The disappointing solution I was able to find was to introduce a Cast
-methods converting my generic methods I only required for nice Api into their <object>
equivalents.
internal ConverterChain<object> Cast() {
return new ConverterChain<object>(input);
}
}
public class Converter<I, O> { ...
internal Converter<object, object> Cast() {
Func<object, List<object>> newC = o => ((List<O>) c.DynamicInvoke(o)).Cast<object>().ToList();
return new ConverterNode<object, object>(newC);
}
}
Those <object>
equivalents could be used to chain my converters and create the resulting data structure :-/
英文:
The disappointing solution I was able to find was to introduce a Cast
-methods converting my generic methods I only required for nice Api into their <object>
equivalents.
public class ConverterChain<I> {
...
internal ConverterChain<object> Cast() {
return new ConverterChain<object>(input);
}
}
public class Converter<I, O> {
...
internal Converter<object, object> Cast() {
Func<object, List<object>> newC = o => ((List<O>) c.DynamicInvoke(o)).Cast<object>().ToList();
return new ConverterNode<object, object>(newC);
}
}
Those <object>
equivalents could be used to chain my converters and create the resulting data structure :-/
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论