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

时间:2010-11-03 04:39:26

标签: c# delegates

我需要将Action<string>投射到Action<object>。虽然这通常是类型不安全的,但在我的情况下,它总是会被一个字符串调用。我收到了这个错误:

Unable to cast object of type 'System.Action1[System.String]' to type 'System.Action1[System.Object]'.

任何线索?反思是公平的游戏。将一个代表包装到另一个代表不是

更新: 我在Creating an performant open delegate for an property setter or getter创建了一个新问题,更好地解释了我的问题,并使用了我希望改进的包装解决方案

4 个答案:

答案 0 :(得分:6)

首先,它不安全。可以接受任何字符串的东西不能(必然)接受任何对象。想想一个示例方法:

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

显然,如果s是没有Trim的对象,则会失败。

从技术上讲,这意味着Action在T上是逆变的。您可以为假设的Action<string>引用分配Action<SubclassString>,但string不能被子类化。

C#允许定期不安全的强制转换(例如object本身为string),但风险为InvalidCastException。但是,他们选择不为不安全的委托演员实施基础设施。

编辑:如果没有包装,我不知道如何做到这一点。

答案 1 :(得分: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
}

答案 2 :(得分:1)

对于迟到的磕磕绊绊,但我今天正在寻找一种方法来做这件事并偶然发现这篇文章。实际上,我发现这样做的唯一简单方法是将Action<string>包裹在新的Action<object>中。在我的例子中,我将我的动作推入并发字典,然后按类型检索它们。

实际上,我正在处理一个消息队列,其中可以定义操作来处理具有特定类型输入的消息。

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);

答案 3 :(得分: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 });

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