egametang/ET · error · Exception
不能把{value.GetType()}转换为{typeof (T)}
Error message
不能把{value.GetType()}转换为{typeof (T)} What it means
Thrown inside the catch of BTEnv.TryGetStruct<T> when the dict value stored under the key cannot be cast to IValue<T>. Structs are boxed in ValueTypeWrap<T> (an IValue<T>) at Add time; a mismatch between the T used at AddStruct and the T used at TryGetStruct produces an InvalidCastException. Note this is a 'Try' method that returns false only for null-key and missing-key — it THROWS on type mismatch, which is a common surprise.
Source
Thrown at Packages/cn.etetet.behaviortree/Scripts/Model/Share/BTEnv.cs:115
if (key == null)
{
return false;
}
if (!this.dict.TryGetValue(key, out object value))
{
return false;
}
try
{
IValue<T> iValue = (IValue<T>) value;
t = iValue.Value;
return true;
}
catch (InvalidCastException e)
{
throw new Exception($"不能把{value.GetType()}转换为{typeof (T)}", e);
}
}
public T GetStruct<T>(string key) where T : struct
{
if (!this.dict.TryGetValue(key, out object value))
{
throw new Exception($"btenv not found key: {key} {typeof(T).FullName}");
}
try
{
IValue<T> iValue = (IValue<T>) value;
return iValue.Value;
}
catch (InvalidCastException e)
{
throw new Exception($"不能把{value.GetType()}转换为{typeof (T)}", e);View on GitHub (pinned to 5cab01f7a8)
Solutions
- Make the generic type argument T identical at AddStruct and TryGetStruct for the same key.
- Audit all call sites for that key and align the types.
- Because TryGetStruct throws on mismatch (does not return false), wrap it in try/catch or pre-check with ContainKey plus a known-type contract.
- Introduce a shared constant/helper that pins the type per key to prevent drift.
Example fix
// before
env.AddStruct<int>("hp", 100);
bool ok = env.TryGetStruct<float>("hp", out float hp); // throws [41]
// after
env.AddStruct<int>("hp", 100);
bool ok = env.TryGetStruct<int>("hp", out int hp); Defensive patterns
Strategy: type-guard
Validate before calling
// No public API exposes the stored struct type; enforce a single T per key via a shared accessor. // Before reading, ensure AddStruct used the identical T for this key.
Type guard
// ValueTypeWrap<T> is internal-boxed; callers cannot inspect it directly.
// Discipline: pair every AddStruct<T>(k) with TryGetStruct<T>(k) using the same T.
static void AddHp(BTEnv env, int v) => env.AddStruct("hp", v);
static bool TryGetHp(BTEnv env, out int v) => env.TryGetStruct("hp", out v); Try / catch
try { env.TryGetStruct<int>("hp", out int hp); }
catch (Exception e) when (e.Message.Contains("转换为")) { /* type mismatch for key */ } Prevention
- Pin one generic type per key through a shared accessor helper.
- Remember Try-prefixed methods throw on type mismatch — they are not exception-free.
- When refactoring a struct type, update both Add and Get call sites together.
When it happens
Trigger: Calling AddStruct<int>(key, 100) then TryGetStruct<float>(key, out float v), or storing with one numeric type and reading with another (int vs long, int vs float). The stored ValueTypeWrap<Int32> is not assignable to IValue<Single>.
Common situations: Numeric type drift between the node that writes the struct (e.g. writing an int HP) and the node that reads it (expecting a long or float); refactor changed the generic type at one call site but not the other; copy-paste of node code with a different generic argument.
Related errors
- btenv not found key: {key} {typeof(T).FullName}
- condition root child count error: {node.Children.Count}
- unknown condition compare op: {op}
- condition number parse error: {text}
- condition token error at {this.index}: {c}
AI-assisted analysis of egametang/ET@5cab01f7a8 (2026-08-13).
Data as JSON: /api/errors/8597482dfe0f897c.
Report an issue: GitHub.