没有重载或泛型的 C# 多参数类型
C# Multiple Argument Types without Overloading or Generics
处理存在大量可能的重载但潜在参数不共享有意义的超类型的情况的最佳方法是什么?
例如,考虑以下情况。写出 Outer() 的每个重载变体都是重复的,但我在这里看不到使用泛型的方法,因为可能的输入类型不共享适用于 Inner() 的任何有意义的超类型。
private int Inner(int a) { /*do something*/ }
private int Inner(string a) { /*do something*/ }
private int Inner(string[] a) { /*do something*/ }
public int Outer(int a, int b)
{
Inner(a) + Inner(b);
}
public int Outer(int a, string b)
{
Inner(a) + Inner(b);
}
public int Outer(int a, string[] b)
{
Inner(a) + Inner(b);
}
public int Outer(string a, int b)
{
Inner(a) + Inner(b);
}
// et cetera...
Inner(a) + Inner(a) 是打错了吗?应该是内(a)+内(b)?
话虽这么说,但这取决于您尝试让该方法做什么。
您可以连接两个整数,添加两个字符串,连接一个字符串和一个 int,将一个 int 添加到 ascii 字符数组,而字符串数组只会使问题复杂化。
然而,如果您希望所有字符串也连接在一起,则字符串数组可能会排除这种情况。
我认为你已经有了最好的解决方案。
您没有任何重复代码。
如果您愿意放弃编译时检查,您可以创建 1 个 public 方法和 2 个对象类型的参数。
您将减少一些单行方法,但会引入许多可能的失败点。
我不建议这样做,坚持你现有的解决方案
更新
这是一个只有一个外部方法的示例
using System;
using System.Linq;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(Outer(1, "s"));
Console.WriteLine(Outer("1", "s"));
Console.ReadLine();
}
private static int FindInner<T>(T value)
{
var type = (from x in Assembly.GetAssembly(typeof(Program)).GetTypes()
where x.BaseType.IsGenericType && x.BaseType == typeof(IInnerManager<>).MakeGenericType(value.GetType())
select x).Single();
return ((IInnerManager<T>)Activator.CreateInstance(type)).Inner(value);
}
public static int Outer<T1, T2>(T1 a, T2 b) { return FindInner(a) + FindInner(b); }
}
public abstract class IInnerManager<T> { public abstract int Inner(T t); }
public class InnerInt : IInnerManager<int> { public override int Inner(int t) { return 1; } }
public class InnerString : IInnerManager<string> { public override int Inner(string t) { return 2; } }
public class InnerStringArray : IInnerManager<string[]> { public override int Inner(string[] t) { return 3; } }
}
像这样的事情怎么样:
public class AggregatedResult<T>
{
private readonly T _value;
public AggregatedResult(T value)
{
this._value = value;
}
pulbic T Value
{
get
{
return this._value;
}
}
public static implicit operator T(AggregatedResult<T> item)
{
if (item == null)
throw new ArgumentNullException(paramName: nameof(item));
return item.Value;
}
}
public class MyAggregatedResult : AggegatedResult<Int32>
{
public MyAggregatedResult(Int32 value) : : base(value)
{
}
public MyAggregatedResult WithInner(int a) { /*do something*/ return new MyAggregatedResult(this.Value + 1); }
public MyAggregatedResult WithInner(string a) { /*do something*/ return new MyAggregatedResult(this.Value + 2); }
public MyAggregatedResult WithInner(string[] a) { /*do something*/ return new MyAggregatedResult(this.Value + 3);}
}
...
var value = new MyAggregatedResult(100);
Console.WriteLine(value.WithInner("").WithInner(new String[0]).Value);
它易于扩展并提供方便的 fluent interface。
如果你需要相同的,但对于 `void` 方法
其实变化不大:
public class ActionAggregate
{
private readonly ActionAggregate _parent;
private readonly Action _action;
public ActionAggregate()
{ }
public ActionAggregate(Action action, ActionAggregate parent)
{
if (action == null)
throw new ArgumentNullException(paramName: nameof(action));
if (parent == null)
throw new ArgumentNullException(paramName: nameof(parent));
this._action = action;
this._parent = parent;
}
public void Invoke()
{
var ancestorsAndSelfFromTheTop = this.GetSelfAndAncestors().Reverse();
foreach (var item in ancestorsAndSelfFromTheTop)
{
if (item._action != null)
item._action();
}
}
private IEnumerable<ActionAggregate> GetSelfAndAncestors()
{
var cur = this;
do
{
yield return cur;
cur = cur._parent;
}
while (cur != null);
}
}
public class MyActionAggregate : ActionAggregate
{
public MyActionAggregate()
{ }
public MyActionAggregate(Action action, ActionAggregate parent) : base(action, parent)
{ }
public MyActionAggregate With(Int32 value)
{
return new MyActionAggregate(
action: () =>
Console.WriteLine($"There was an int value of '{value}'"),
parent: this);
}
public MyActionAggregate With(String value)
{
return new MyActionAggregate(
action: () =>
Console.WriteLine($"There was a string value of '{value}'"),
parent: this);
}
public MyActionAggregate With(String[] value)
{
return new MyActionAggregate(
action: () =>
Console.WriteLine($"There was a string[] value of '{String.Join(", ", value)}'"),
parent: this);
}
}
...
public class BusinessObject
{
public MyActionAggregate GetActionRoot()
{
return new MyActionAggregate();
}
}
...
var obj = new BusinessObject();
obj
.GetActionRoot()
.With(123)
.With("line")
.With(new[] { "1", "2", "3" })
.With(321)
.Invoke();
处理存在大量可能的重载但潜在参数不共享有意义的超类型的情况的最佳方法是什么?
例如,考虑以下情况。写出 Outer() 的每个重载变体都是重复的,但我在这里看不到使用泛型的方法,因为可能的输入类型不共享适用于 Inner() 的任何有意义的超类型。
private int Inner(int a) { /*do something*/ }
private int Inner(string a) { /*do something*/ }
private int Inner(string[] a) { /*do something*/ }
public int Outer(int a, int b)
{
Inner(a) + Inner(b);
}
public int Outer(int a, string b)
{
Inner(a) + Inner(b);
}
public int Outer(int a, string[] b)
{
Inner(a) + Inner(b);
}
public int Outer(string a, int b)
{
Inner(a) + Inner(b);
}
// et cetera...
Inner(a) + Inner(a) 是打错了吗?应该是内(a)+内(b)?
话虽这么说,但这取决于您尝试让该方法做什么。
您可以连接两个整数,添加两个字符串,连接一个字符串和一个 int,将一个 int 添加到 ascii 字符数组,而字符串数组只会使问题复杂化。
然而,如果您希望所有字符串也连接在一起,则字符串数组可能会排除这种情况。
我认为你已经有了最好的解决方案。
您没有任何重复代码。
如果您愿意放弃编译时检查,您可以创建 1 个 public 方法和 2 个对象类型的参数。
您将减少一些单行方法,但会引入许多可能的失败点。
我不建议这样做,坚持你现有的解决方案
更新
这是一个只有一个外部方法的示例
using System;
using System.Linq;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(Outer(1, "s"));
Console.WriteLine(Outer("1", "s"));
Console.ReadLine();
}
private static int FindInner<T>(T value)
{
var type = (from x in Assembly.GetAssembly(typeof(Program)).GetTypes()
where x.BaseType.IsGenericType && x.BaseType == typeof(IInnerManager<>).MakeGenericType(value.GetType())
select x).Single();
return ((IInnerManager<T>)Activator.CreateInstance(type)).Inner(value);
}
public static int Outer<T1, T2>(T1 a, T2 b) { return FindInner(a) + FindInner(b); }
}
public abstract class IInnerManager<T> { public abstract int Inner(T t); }
public class InnerInt : IInnerManager<int> { public override int Inner(int t) { return 1; } }
public class InnerString : IInnerManager<string> { public override int Inner(string t) { return 2; } }
public class InnerStringArray : IInnerManager<string[]> { public override int Inner(string[] t) { return 3; } }
}
像这样的事情怎么样:
public class AggregatedResult<T>
{
private readonly T _value;
public AggregatedResult(T value)
{
this._value = value;
}
pulbic T Value
{
get
{
return this._value;
}
}
public static implicit operator T(AggregatedResult<T> item)
{
if (item == null)
throw new ArgumentNullException(paramName: nameof(item));
return item.Value;
}
}
public class MyAggregatedResult : AggegatedResult<Int32>
{
public MyAggregatedResult(Int32 value) : : base(value)
{
}
public MyAggregatedResult WithInner(int a) { /*do something*/ return new MyAggregatedResult(this.Value + 1); }
public MyAggregatedResult WithInner(string a) { /*do something*/ return new MyAggregatedResult(this.Value + 2); }
public MyAggregatedResult WithInner(string[] a) { /*do something*/ return new MyAggregatedResult(this.Value + 3);}
}
...
var value = new MyAggregatedResult(100);
Console.WriteLine(value.WithInner("").WithInner(new String[0]).Value);
它易于扩展并提供方便的 fluent interface。
如果你需要相同的,但对于 `void` 方法
其实变化不大:
public class ActionAggregate
{
private readonly ActionAggregate _parent;
private readonly Action _action;
public ActionAggregate()
{ }
public ActionAggregate(Action action, ActionAggregate parent)
{
if (action == null)
throw new ArgumentNullException(paramName: nameof(action));
if (parent == null)
throw new ArgumentNullException(paramName: nameof(parent));
this._action = action;
this._parent = parent;
}
public void Invoke()
{
var ancestorsAndSelfFromTheTop = this.GetSelfAndAncestors().Reverse();
foreach (var item in ancestorsAndSelfFromTheTop)
{
if (item._action != null)
item._action();
}
}
private IEnumerable<ActionAggregate> GetSelfAndAncestors()
{
var cur = this;
do
{
yield return cur;
cur = cur._parent;
}
while (cur != null);
}
}
public class MyActionAggregate : ActionAggregate
{
public MyActionAggregate()
{ }
public MyActionAggregate(Action action, ActionAggregate parent) : base(action, parent)
{ }
public MyActionAggregate With(Int32 value)
{
return new MyActionAggregate(
action: () =>
Console.WriteLine($"There was an int value of '{value}'"),
parent: this);
}
public MyActionAggregate With(String value)
{
return new MyActionAggregate(
action: () =>
Console.WriteLine($"There was a string value of '{value}'"),
parent: this);
}
public MyActionAggregate With(String[] value)
{
return new MyActionAggregate(
action: () =>
Console.WriteLine($"There was a string[] value of '{String.Join(", ", value)}'"),
parent: this);
}
}
...
public class BusinessObject
{
public MyActionAggregate GetActionRoot()
{
return new MyActionAggregate();
}
}
...
var obj = new BusinessObject();
obj
.GetActionRoot()
.With(123)
.With("line")
.With(new[] { "1", "2", "3" })
.With(321)
.Invoke();