将Action<string>转换为Action<object>

3
我需要将一个Action<string>强制转换为Action<object>。虽然这一般来说是不安全的,但在我的情况下,它总是会被调用一个字符串。我遇到了这个错误: 无法将类型为 'System.Action<string>' 的对象强制转换为类型 'System.Action<object>'。 有任何线索吗?使用反射是可行的。包装一个委托到另一个中不是可行的解决方案。
更新: 我在Creating an performant open delegate for an property setter or getter创建了一个新的问题,并更好地解释了我的问题,并提出了一个使用包装方法的解决方案,我想改进它。

3
抱歉,但CLR没有一种方法可以在不将一个委托包装到另一个委托中的情况下完成这个任务。 - Gabe
包含为什么要这样做的原因可能会有所帮助(与第三方库一起工作,好奇心等),因为正如Gabe所提到的那样,如果不将其包装在另一个委托中,则无法实现此操作。然而,可能存在解决您可能遇到的潜在问题的解决方案。 - John Rasch
有反对将委托包装在其他内容中的特别原因吗?这比使用反射要直接得多... - RameshVel
4个回答

6

首先,这不安全。能够接受任何字符串的东西未必能接受任何对象。想象一个例子方法:

void method(String s)
{
  s.Trim();
}

显然,如果s是没有“Trim”方法的对象,这将失败。
从技术上讲,这意味着“Action”在T上是逆变的。你可以将一个“Action<string>”赋值给一个假想的“Action<SubclassString>”引用,但“string”不能被子类化。
的确,C#允许常规的不安全转换(例如,从“object”本身到“string”),但存在“InvalidCastException”的风险。然而,他们选择不实现不安全委托转换的基础设施。
编辑:我不知道是否有一种方法可以在没有包装器的情况下完成它。

当然。我在问题中添加了澄清。我的合同确保它只能使用字符串调用。因此,强制转换确实是我需要的。 - David Reis
可以请版主删除这个回答吗?它对我没有帮助,也可能会阻止其他人查看问题。 - David Reis
2
@David,当我回答这个问题时,它说:“我需要将Action<string>转换为Action<object>。理论上应该是可能的”,而我解释了为什么这是错误的。即使现在,问题的标题也不清楚。 - Matthew Flaschen
@Mathew,为什么不清楚呢?有没有更明确的替代方案? - David Reis
一个实际的强制转换或赋值是不可能的,我已经解释过了。由于您说包装器不可用,很难给出一个好的替代方案。 - Matthew Flaschen

1

我知道你的帖子指定在另一个委托中包装不是一个选项,但不幸的是这确实是你在这里最好的选择。CLR根本不允许在这个方向上的委托之间进行转换。任何反射都无法解决这个问题。你能进一步阐述为什么这不是一个选项吗?

原因是它会创建类型安全问题,因为调用者可以将任何对象传递到 Action<object> 中,而 Action<string> 只能处理字符串。即使你的代码只传递了一个 string,CLR也不能保证这一点,因此不允许进行不安全的转换。

我能想到的下一个最佳选择是将被包装在 Action<string> 中的原始方法从接受 string 类型的参数更改为接受 object 类型的参数。然后手动验证类型是否为 string。例如:

// Original Version
void Method(string str) {
  // Operate on the string
}

// Modified version
void Method(object obj) { 
  string str = (string)obj;
  // operate on the string
}

1
抱歉回复晚了,但我今天正在寻找一种方法来做这件事,并偶然发现了这篇文章。实际上,我找到的唯一简单的方法是将 Action<string> 包装在新的 Action<object> 中。在我的情况下,我将我的操作推入一个 Concurrent Dictionary 中,然后按类型检索它们。有效地,我正在处理一个消息队列,其中可以定义操作以处理具有特定输入类型的消息。
ConcurrentDictionary<Type, Action<object>> _actions = new ConcurrentDictionary<Type, Action<object>>();
Action<string> actionStr = s => Console.WriteLine(s);

var actionObj = new Action<object>(obj => { 
  var castObj = (V)Convert.ChangeType(obj, typeof(V)); actionStr(castObj); 
} );

_actions.TryAdd(typeof(string), actionObj);

0

既然反射是公平的游戏,我建议如下。如果您跟踪一个对象、MethodInfo和可能针对其执行的参数,则可以避免强制转换问题。该概念的工作原理示例如下:

Action<string> s;
Action<object> o;

object sTarget = s.Target;
object oTarget = o.Target;

MethodInfo sMethod = s.Method;
MethodInfo oMethod = o.Method;

// Time to invoke in a later time.
sMethod.Invoke(sTarget, new object[] { strVal });
oMethod.Invoke(oTarget, new object[] { objVal });

唯一的危险是可能会针对错误的方法执行错误的参数类型。您可能需要在此处进行一些簿记以防止这种情况发生。但是,在您的情况下,由于保证将传递一个字符串(并且由于字符串始终是一个对象),因此这应该始终成功。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接