1

我需要执行以下操作:

  1. 获取类型 T 的参数并将其设置为当前。
  2. 检查 (current).Parent 是否存在并且是 A 类型,如果是,去那里。
  3. 检查 (current).Owner 是否存在并且属于 B 类型,如果存在,则去那里。
  4. 检查是否(当前)。无论是否存在并且属于 C 类型,如果是,则去那里。
  5. 返回(当前)。

这相当于

C c = ((param).Parent as A).Owner as B).Whatever as C;

有一些安全性来检查任何“步骤”是否为空。

问题是我想遍历完全不同的类型和完全不同的属性。我需要在我的结构的几个不同的地方这样做。这可以以某种方式自动化 - 也许使用 lambdas、动态、某种泛型类?我在想类似的东西:

C c = Util.Traverse<C>(someObj, new[]
{
    new Step { Path = p => p.Parent, ExpectedType = typeof(A) },
    new Step { Path = p => p.Owner, ExpectedType = typeof(B) },
    new Step { Path = p => p.Whatever, ExpectedType = typeof(C) }
});
4

5 回答 5

3

空传播表达式(即它的计算结果为 ,而不是 NRE null)是一些已经被非正式地踢过几次的东西,但它根本不是 C# 中当前存在的一个特性。

没有一个很好的方法来写这个。“丑陋的单一陈述”版本将是:

A a;
B b;
C c;
// ...
return (a = param.Parent as A) == null ? null :
       (b = a.Owner as B) == null ? null :
       (c = b.Whatever as C) == null ? null :
       // ...
       c.FinalThing;

就个人而言,我只是将它分成很多部分(可能在辅助方法中):

A a = param.Parent as A;
if(a==null) return null;
B b = a.Owner as B;
if(b==null) return null;
C c = b.Whatever as C;
if(c==null) return null;
// ...
return c.FinalThing;
于 2013-11-06T12:19:01.303 回答
2

如果您经常使用它并且有时必须使用自定义值而不是 null 您可以编写如下扩展方法:

public static class Helper
{
    public static U SafeCast<T, U>(this T obj, Expression<Func<T, object>> memberExpression, U defaultValue)
        where T : class
        where U : class
    {
        if (obj == null)
        {
            return defaultValue;
        }

        var me = memberExpression.Body as MemberExpression;
        if (me == null)
        {
            throw new ArgumentException("memberExpression must be MemberExpression");
        }

        // TODO : Check for fields, not only properties
        var memberValue = obj.GetType().GetProperty(me.Member.Name).GetValue(obj) as U;
        if (memberValue != null)
        {
            return memberValue;
        }

        return defaultValue;
    }
}

并以这种方式使用它:

var someObj = new SomeObj();
var defB = new B();
var res = someObj.SafeCast(a => a.Parent, default(A)).SafeCast(a => a.Owner, defB).SafeCast(a => a.Whatever, default(C));
于 2013-11-06T12:29:24.460 回答
1

我认为你可以使用 lambdas 来做到这一点,例如

Func<T, A> step1 = p => (p.Parent != null && p.Parent is A) ? p.Parent as A : null;
Func<A, B> step2 = p => (p.Owner != null && p.Owner is B) ? p.Owner as B : null;
Func<B, C> step3 = p => (p.Whatever != null && p.Whatever is C) ? p.Whatever as C : null;
...
A a = step1(someObj) != null;
B b;
C c;
if (a != null)
    b = step2(a);
if (b != null)
    c = step3(b);
return c;
于 2013-11-06T12:20:26.667 回答
1

那这个呢?

public static void CastAndContinue<T>(object toCast, Action<T> nextStep)
{
  if (toCast is T)
    nextStep((T)toCast);
}

调用为:

//(param).Parent as A).Owner as B).Whatever as C
finalThing localResult = null;
CastAndContinue<A>(param.Parent, 
    p => CastAndContinue<B>(p.Owner, 
    x => CastAndContinue<C>(x.Whatever, z=> finalThing = z)
于 2013-11-06T12:33:59.563 回答
0

在您的指导下,我编写了两种不同的解决方案:

使用动态和 lambda:

public struct Step {

    public Func<dynamic, dynamic> Path;
    public Type ExpectedType;
}

public static class StructureHelper {

    public static T Traverse<T>(dynamic obj, Step[] steps) {

        dynamic current = obj;
        for (int i = 0; i < steps.Length; i++) {

            if (current == null)
                return default(T);

            dynamic next = steps[i].Path(current);
            if (next.GetType() != steps[i].ExpectedType)
                return default(T);

            current = next;
        }

        if (current.GetType() == typeof(T))
            return (T)current;
        else
            return default(T);
    }
}

用法:

C c = StructureHelper.Traverse<C>(obj,
    new []
    {
        new Step { Path = p => p.Owner, ExpectedType = typeof(A) },
        new Step { Path = p => p.Parent, ExpectedType = typeof(B) },
        new Step { Path = p => p.Whatever, ExpectedType = typeof(C) }
    });

使用 lambda 和类助手

public static U Traverse<T, U>(this T obj, Func<T, U> Step) 
    where T : class
    where U : class {

    if (obj == null)
        return null;

    U result = Step(obj);
    return result;
}

用法:

C c = obj.Traverse(p => p.Parent as A)
    .Traverse(p => p.Owner as B)
    .Traverse(p => p.Whatever as C);
于 2013-11-06T13:08:51.087 回答