'DoSomethingInt' 没有重载匹配委托 'DoSomethingDelegate<T>'
No overload for 'DoSomethingInt' matches delegate 'DoSomethingDelegate<T>'
class DelegateTest<T> where T: // ....
{
public DelegateTest(DoSomethingDelegate<T> action = null)
{
if (action != null)
_doSomething = action;
else if (typeof(T) == typeof(int))
//_doSomething = DoSomethingInt; // fails
//_doSomething = (DoSomethingDelegate<T>)DoSomethingInt; // fails
//_doSomething = (DoSomethingDelegate<T>)((Delegate)DoSomethingInt); // fails
_doSomething = (val) => DoSomethingInt((int)((object)val)); // too ugly
else // ...
}
public void DoSomethingVeryComplex(T val)
{
// ....
_doSomething(val);
// ....
}
DoSomethingDelegate<T> _doSomething;
static void DoSomethingInt(int val)
{
}
}
delegate void DoSomethingDelegate<T>(T val);
因此,委托和方法在签名上是兼容的,但是由于某些不明原因,此分配不起作用,而且我完全不知道如何让它起作用。除了创建 shim 函数之外还有更好的想法吗?
这个问题不是关于如何用其他方式重写这段代码的问题。这只是关于如何完成这项任务,如果那是不可能的 - 为什么。
如果您使用 Action<T>
而不是自定义委托,则可以直接转换和分配委托。
class DelegateTest<T>
{
public DelegateTest()
{
if (typeof(T) == typeof(int))
{
_doSomething = (Action<T>)(object)DoSomethingInt;
}
}
Action<T> _doSomething;
public void InvokeDoSomething(T x) => _doSomething(x);
static void DoSomethingInt(int val)
{
Console.WriteLine("Hello world! The argument was {0}", val);
}
}
快速测试:
public class Program
{
public static async Task Main()
{
var o = new DelegateTest<int>();
o.InvokeDoSomething(1);
}
}
输出:
Hello world! The argument was 1
类似于 John Wu 的 ,但也适用于 DoSomethingDelegate<T>
:
static void Main()
{
new DelegateTest<int>().InvokeDoSomething(1);
new DelegateTest<string>().InvokeDoSomething("Hello");
}
delegate void DoSomethingDelegate<T>(T val);
class DelegateTest<T>
{
public DelegateTest()
{
if (typeof(T) == typeof(int))
_doSomething = new DoSomethingDelegate<T>((Action<T>)(object)DoSomethingInt);
else if (typeof(T) == typeof(string))
_doSomething = new DoSomethingDelegate<T>((Action<T>)(object)DoSomethingString);
else throw new NotSupportedException();
}
DoSomethingDelegate<T> _doSomething;
public void InvokeDoSomething(T x) => _doSomething(x);
void DoSomethingInt(int val) => Console.WriteLine($"DoSomethingInt({val})");
void DoSomethingString(string val) => Console.WriteLine($"DoSomethingString({val})");
}
输出:
DoSomethingInt(1)
DoSomethingString(Hello)
备选方案 1: 正如 Enigmativity 在 中提到的,将 DoSomethingInt
转换为 DoSomethingDelegate<T>
也可以这样实现:
public DelegateTest()
{
if (typeof(T) == typeof(int))
_doSomething = (DoSomethingDelegate<T>)(Delegate)(DoSomethingDelegate<int>)DoSomethingInt;
else if (typeof(T) == typeof(string))
_doSomething = (DoSomethingDelegate<T>)(Delegate)(DoSomethingDelegate<string>)DoSomethingString;
else throw new NotSupportedException();
}
与我最初的建议相比,它还使委托的调用速度提高了大约 20%,原因由 Matthew Watson 解释。我最初的建议涉及两次方法调用,而不是一次。
备选方案 2: John Wu 的变体,使用 switch
语句而不是 if
+as
:
public DelegateTest()
{
switch (this)
{
case DelegateTest<int> self: self._doSomething = DoSomethingInt; break;
case DelegateTest<string> self: self._doSomething = DoSomethingString; break;
default: throw new NotSupportedException();
}
}
这个是类型安全的。您不能意外地将错误的方法分配给错误的类型。调用委托与之前的替代方案 1 一样快。
首先赋值的问题是,您在 DelegateTest<T>
的范围内,其中 T
可以是任何类型,并且您要求编译器允许某些类型DoSomethingDelegate<int>
分配给 DoSomethingDelegate<T>
。如果 T
在 run-time 处是 int
,那很好,但编译器不知道这一点。尽管 run-time 检查了类型,但这是非法转换。
您能做的最好的事情就是让您的代码对编码错误更有弹性。这是 Theo 答案的变体,它使代码更多 strongly-typed。这可能是您无需反思就能得到的最好结果。
static void Main()
{
new DelegateTest<int>().InvokeDoSomething(1);
new DelegateTest<string>().InvokeDoSomething("Hello");
}
delegate void DoSomethingDelegate<T>(T val);
class DelegateTest<T>
{
private Dictionary<Type, Delegate> _delegates = new Dictionary<Type, Delegate>();
private void Register<K>(DoSomethingDelegate<K> @delegate)
{
_delegates[typeof(K)] = @delegate;
}
public DelegateTest()
{
this.Register<int>(DoSomethingInt);
this.Register<string>(DoSomethingString);
if (_delegates.ContainsKey(typeof(T)))
{
_doSomething = (DoSomethingDelegate<T>)_delegates[typeof(T)];
}
else throw new NotSupportedException();
}
DoSomethingDelegate<T> _doSomething;
public void InvokeDoSomething(T x) => _doSomething(x);
void DoSomethingInt(int val) => Console.WriteLine($"DoSomethingInt({val})");
void DoSomethingString(string val) => Console.WriteLine($"DoSomethingString({val})");
}
输出:
DoSomethingInt(1)
DoSomethingString(Hello)
因为你知道 T
,你可以将 this
转换为特定的泛型类型 (DelegateTest<int>
)。获得该引用后,您可以使用它 type-safe 和 type-specific 访问其方法、属性和字段,甚至是私有的。
class DelegateTest<T>
{
public DelegateTest()
{
if (typeof(T) == typeof(int))
{
var self = this as DelegateTest<int>; //Magic!!
self._doSomething = DoSomethingInt;
}
}
DoSomethingDelegate<T> _doSomething;
public void InvokeDoSomething(T x) => _doSomething(x);
static void DoSomethingInt(int val)
{
Console.WriteLine("Hello. The argument was {0}", val);
}
}
delegate void DoSomethingDelegate<T>(T val);
快速测试:
public class Program
{
public static async Task Main()
{
var o = new DelegateTest<int>();
o.InvokeDoSomething(1);
}
}
输出:
Hello. The argument was 1
class DelegateTest<T> where T: // ....
{
public DelegateTest(DoSomethingDelegate<T> action = null)
{
if (action != null)
_doSomething = action;
else if (typeof(T) == typeof(int))
//_doSomething = DoSomethingInt; // fails
//_doSomething = (DoSomethingDelegate<T>)DoSomethingInt; // fails
//_doSomething = (DoSomethingDelegate<T>)((Delegate)DoSomethingInt); // fails
_doSomething = (val) => DoSomethingInt((int)((object)val)); // too ugly
else // ...
}
public void DoSomethingVeryComplex(T val)
{
// ....
_doSomething(val);
// ....
}
DoSomethingDelegate<T> _doSomething;
static void DoSomethingInt(int val)
{
}
}
delegate void DoSomethingDelegate<T>(T val);
因此,委托和方法在签名上是兼容的,但是由于某些不明原因,此分配不起作用,而且我完全不知道如何让它起作用。除了创建 shim 函数之外还有更好的想法吗?
这个问题不是关于如何用其他方式重写这段代码的问题。这只是关于如何完成这项任务,如果那是不可能的 - 为什么。
如果您使用 Action<T>
而不是自定义委托,则可以直接转换和分配委托。
class DelegateTest<T>
{
public DelegateTest()
{
if (typeof(T) == typeof(int))
{
_doSomething = (Action<T>)(object)DoSomethingInt;
}
}
Action<T> _doSomething;
public void InvokeDoSomething(T x) => _doSomething(x);
static void DoSomethingInt(int val)
{
Console.WriteLine("Hello world! The argument was {0}", val);
}
}
快速测试:
public class Program
{
public static async Task Main()
{
var o = new DelegateTest<int>();
o.InvokeDoSomething(1);
}
}
输出:
Hello world! The argument was 1
类似于 John Wu 的 DoSomethingDelegate<T>
:
static void Main()
{
new DelegateTest<int>().InvokeDoSomething(1);
new DelegateTest<string>().InvokeDoSomething("Hello");
}
delegate void DoSomethingDelegate<T>(T val);
class DelegateTest<T>
{
public DelegateTest()
{
if (typeof(T) == typeof(int))
_doSomething = new DoSomethingDelegate<T>((Action<T>)(object)DoSomethingInt);
else if (typeof(T) == typeof(string))
_doSomething = new DoSomethingDelegate<T>((Action<T>)(object)DoSomethingString);
else throw new NotSupportedException();
}
DoSomethingDelegate<T> _doSomething;
public void InvokeDoSomething(T x) => _doSomething(x);
void DoSomethingInt(int val) => Console.WriteLine($"DoSomethingInt({val})");
void DoSomethingString(string val) => Console.WriteLine($"DoSomethingString({val})");
}
输出:
DoSomethingInt(1)
DoSomethingString(Hello)
备选方案 1: 正如 Enigmativity 在 DoSomethingInt
转换为 DoSomethingDelegate<T>
也可以这样实现:
public DelegateTest()
{
if (typeof(T) == typeof(int))
_doSomething = (DoSomethingDelegate<T>)(Delegate)(DoSomethingDelegate<int>)DoSomethingInt;
else if (typeof(T) == typeof(string))
_doSomething = (DoSomethingDelegate<T>)(Delegate)(DoSomethingDelegate<string>)DoSomethingString;
else throw new NotSupportedException();
}
与我最初的建议相比,它还使委托的调用速度提高了大约 20%,原因由 Matthew Watson
备选方案 2: John Wu switch
语句而不是 if
+as
:
public DelegateTest()
{
switch (this)
{
case DelegateTest<int> self: self._doSomething = DoSomethingInt; break;
case DelegateTest<string> self: self._doSomething = DoSomethingString; break;
default: throw new NotSupportedException();
}
}
这个是类型安全的。您不能意外地将错误的方法分配给错误的类型。调用委托与之前的替代方案 1 一样快。
首先赋值的问题是,您在 DelegateTest<T>
的范围内,其中 T
可以是任何类型,并且您要求编译器允许某些类型DoSomethingDelegate<int>
分配给 DoSomethingDelegate<T>
。如果 T
在 run-time 处是 int
,那很好,但编译器不知道这一点。尽管 run-time 检查了类型,但这是非法转换。
您能做的最好的事情就是让您的代码对编码错误更有弹性。这是 Theo 答案的变体,它使代码更多 strongly-typed。这可能是您无需反思就能得到的最好结果。
static void Main()
{
new DelegateTest<int>().InvokeDoSomething(1);
new DelegateTest<string>().InvokeDoSomething("Hello");
}
delegate void DoSomethingDelegate<T>(T val);
class DelegateTest<T>
{
private Dictionary<Type, Delegate> _delegates = new Dictionary<Type, Delegate>();
private void Register<K>(DoSomethingDelegate<K> @delegate)
{
_delegates[typeof(K)] = @delegate;
}
public DelegateTest()
{
this.Register<int>(DoSomethingInt);
this.Register<string>(DoSomethingString);
if (_delegates.ContainsKey(typeof(T)))
{
_doSomething = (DoSomethingDelegate<T>)_delegates[typeof(T)];
}
else throw new NotSupportedException();
}
DoSomethingDelegate<T> _doSomething;
public void InvokeDoSomething(T x) => _doSomething(x);
void DoSomethingInt(int val) => Console.WriteLine($"DoSomethingInt({val})");
void DoSomethingString(string val) => Console.WriteLine($"DoSomethingString({val})");
}
输出:
DoSomethingInt(1)
DoSomethingString(Hello)
因为你知道 T
,你可以将 this
转换为特定的泛型类型 (DelegateTest<int>
)。获得该引用后,您可以使用它 type-safe 和 type-specific 访问其方法、属性和字段,甚至是私有的。
class DelegateTest<T>
{
public DelegateTest()
{
if (typeof(T) == typeof(int))
{
var self = this as DelegateTest<int>; //Magic!!
self._doSomething = DoSomethingInt;
}
}
DoSomethingDelegate<T> _doSomething;
public void InvokeDoSomething(T x) => _doSomething(x);
static void DoSomethingInt(int val)
{
Console.WriteLine("Hello. The argument was {0}", val);
}
}
delegate void DoSomethingDelegate<T>(T val);
快速测试:
public class Program
{
public static async Task Main()
{
var o = new DelegateTest<int>();
o.InvokeDoSomething(1);
}
}
输出:
Hello. The argument was 1