繁体   English   中英

使用表达式树修改字符串和子类/接口字符串属性

Using Expression Trees to modify String and Child Class/Interface String Properties

提示:本站收集StackOverFlow近2千万问答,支持中英文搜索,鼠标放在语句上弹窗显示对应的参考中文或英文, 本站还提供   中文繁体   英文版本   中英对照 版本,有任何建议请联系yoyou2525@163.com。

我精通表达式树的基础知识,但我在执行此操作时遇到了困难:

我需要通过查找每个string属性并对其进行加密来修改类/接口的实例。 我还需要递归地对每个“子”类/接口属性执行相同的操作,一直到兔子洞。 这包括IEnumerable<T> where T: class以及。 我已经对加密进行了排序,但是坦率地说,创建表达式尝试为传入的任何T执行此操作超出了我的理解。

我尝试通过反射来实现这一点,但性能很快就成了一个问题——这就是我到目前为止所得到的:

使用这篇文章中的AggregateHelperPropertyHelper类,我能够执行基本的string加密:

简单的测试类:

public class SimpleEncryptionTest
{
   public string String1 { get; set; }
}

public class NestedClassEncryptionTest
{
   public string SomeString { get; set; }
   public SimpleEncryptionTest Inner { get; set; } 
}

public class ListClassEncryptionTest
{
    public List<string> StringList { get; set; }
}

我用来验证结果的单元测试:

[TestMethod]
public void Encryption_works_on_a_simple_class()
{
    var testString = "this is only a test. If this had been a real emergency...";
    var sut = new SimpleEncryptionTest() { String1 = testString };
    sut.EncryptStringProperties();

    Assert.AreNotEqual(testString, sut.String1);
}

[TestMethod]
public void Round_trip_works_on_a_simple_class()
{
    var testString = "what string should I use?";
    var sut = new SimpleEncryptionTest() { String1 = testString };

    sut.EncryptStringProperties();
    sut.DecryptStringProperties();

    Assert.AreEqual(testString, sut.String1);
}

[TestMethod]
public void Round_trip_works_in_nested_class_scenario()
{
    var outerString = "what is your name?";
    var innerString = "Tony; what's your name?";

    var sut = new NestedClassEncryptionTest{
        SomeString = outerString,
        Inner = new SimpleEncryptionTest(){String1 = innerString }
    };

    sut.EncryptStringProperties();

    Assert.AreNotEqual(outerString, sut.SomeString);
    Assert.AreNotEqual(innerString, sut.Inner.String1);

    sut.DecryptStringProperties();

    Assert.AreEqual(outerString, sut.SomeString);
    Assert.AreEqual(innerString, sut.Inner.String1);
}

[TestMethod]
public void Round_trip_works_on_lists()
{
    var testone = "one";
    var testtwo = "two";
    var testStrings = new List<string>() { testone, testtwo };
    var sut = new ListClassEncryptionTest() { StringList = testStrings };

    sut.EncryptStringProperties();

    Assert.AreNotEqual(testone, sut.StringList[0]);
    Assert.AreNotEqual(testtwo, sut.StringList[1]);

    sut.DecryptStringProperties();

    Assert.AreEqual(testone, sut.StringList[0]);
    Assert.AreEqual(testtwo, sut.StringList[1]);
}

以及我用来加密/解密值的扩展方法:

/// <summary>
/// Iterates through an Object's properties and encrypts any strings it finds, 
/// recursively iterating any class or interface Properties.
/// </summary>
/// <typeparam name="T">Any type</typeparam>
/// <param name="genericObj">The object to encrypt</param>
/// <returns>The original object, with its string values encrypted.</returns>
public static T EncryptStringProperties<T>(this T obj)
    where T : class
{
    return Crypt(obj, EncryptStringAction);
}

/// <summary>
/// Iterates through an Object's properties and decrypts any strings it finds, 
/// recursively iterating any class or interface Properties.
/// </summary>
/// <typeparam name="T">Any type</typeparam>
/// <param name="genericObj">The object to decrypt</param>
/// <returns>The original object, with its string values decrypted.</returns>
public static T DecryptStringProperties<T>(this T obj)
{
    return Crypt(obj, DecryptStringAction);
}

private static T Crypt<T>(T obj, Action<PropertyHelper<T, string>, T> action)
{
    var stringProperties = new AggregateHelper<T, string>();
    foreach (var property in stringProperties.Properties)
    {
        var propertyHelper = stringProperties[property];
        action(propertyHelper, obj);
    }

    // how do I find the nested classes, interfaces and lists
    // using Expression Trees to feed them through the same processing?

    return obj;
}

private static void EncryptStringAction<T>(PropertyHelper<T, string> prop, T genericObj)
{
    var plainTextValue = (string)prop.GetValue(genericObj);
    prop.SetValue(genericObj, plainTextValue.ToEncryptedString());
}

private static void DecryptStringAction<T>(PropertyHelper<T, string> prop, T genericObj)
{
    var encryptedValue = (string)prop.GetValue(genericObj);
    prop.SetValue(genericObj, encryptedValue.ToDecryptedString());
}

就它而言,这很有效; 它加密任何object上的string属性,但我需要更进一步 - 我需要一些方法来递归 go “兔子洞” - 创建一个AggregateHelper选择作为实例对象(类或接口)的属性并通过.EncryptStringProperties()扩展方法,以及处理任何带有string值的IEnumerable

1 个回复

尝试以下扩展。 它为每个 object 生成替换表达式树,并编译代表以进行快速字符串替换。 所有代表都被缓存,替换应该很快。 它还处理递归引用并且应该省略 Stack Overflow。

StringPropReplacer.ReplaceStrings(obj, s => Encrypt(s));
public static class StringPropReplacer
{
    private static ParameterExpression _strParam = Expression.Parameter(typeof(string), "str");
    private static ParameterExpression _originalValue = Expression.Variable(typeof(string), "original");
    private static ParameterExpression _newValue = Expression.Variable(typeof(string), "newValue");
    private static ParameterExpression _visitedParam = Expression.Parameter(typeof(HashSet<object>), "visited");
    private static ParameterExpression _replacerParam = Expression.Parameter(typeof(Func<string, string>), "replacer");
    private static ParameterExpression[] _blockVariables = new [] {_originalValue, _newValue};

    private static void ReplaceObject<T>(T obj, HashSet<object> visited, Func<string, string> replacer)
    {
        ReflectionHolder<T>.ReplaceObject(obj, visited, replacer);
    }

    private static void ReplaceObjects<T>(IEnumerable<T> objects, HashSet<object> visited, Func<string, string> replacer)
    {
        ReflectionHolder<T>.ReplaceObjects(objects, visited, replacer);
    }

    public class ObjectReferenceEqualityComparer<T> : IEqualityComparer<T>
        where T : notnull
    {
        public static IEqualityComparer<T> Default = new ObjectReferenceEqualityComparer<T>();

        #region IEqualityComparer<T> Members

        public bool Equals(T x, T y)
        {
            return ReferenceEquals(x, y);
        }

        public int GetHashCode(T obj)
        {
            if (obj == null)
                return 0;

            return RuntimeHelpers.GetHashCode(obj);
        }

        #endregion
    }


    private static class ReflectionHolder<T>
    {
        private static Action<T, HashSet<object>, Func<string, string>> _replacer;

        static ReflectionHolder()
        {
            var objParam = Expression.Parameter(typeof(T), "obj");
            var blockBody = new List<Expression>();

            foreach (var prop in typeof(T).GetProperties())
            {
                if (prop.PropertyType == typeof(string) && prop.CanRead && prop.CanWrite)
                {
                    var propExpression = Expression.MakeMemberAccess(objParam, prop);
                    blockBody.Add(Expression.Assign(_originalValue, propExpression));
                    blockBody.Add(Expression.Assign(_newValue, Expression.Invoke(_replacerParam, _originalValue)));
                    blockBody.Add(Expression.IfThen(Expression.NotEqual(_originalValue, _newValue),
                        Expression.Assign(propExpression, _newValue)));
                }
                else if (typeof(IEnumerable).IsAssignableFrom(prop.PropertyType))
                {
                    var intf = prop.PropertyType
                        .GetInterfaces()
                        .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>));

                    if (intf != null)
                    {
                        var propExpression = Expression.MakeMemberAccess(objParam, prop);
                        blockBody.Add(Expression.Call(typeof(StringPropReplacer), "ReplaceObjects",
                            intf.GetGenericArguments(), propExpression, _visitedParam, _replacerParam
                        ));
                    }
                }
                else if (prop.PropertyType.IsClass)
                {
                    var propExpression = Expression.MakeMemberAccess(objParam, prop);
                    blockBody.Add(Expression.Call(typeof(StringPropReplacer), "ReplaceObject",
                        new[] {prop.PropertyType}, propExpression, _visitedParam, _replacerParam
                    ));
                }
                
            }

            if (blockBody.Count == 0)
                _replacer = (o, v, f) => { };
            else
            {
                var replacerExpr = Expression.Lambda<Action<T, HashSet<object>, Func<string, string>>>(
                    Expression.Block(_blockVariables, blockBody), objParam, _visitedParam, _replacerParam);
                _replacer = replacerExpr.Compile();
            }
        }

        public static void ReplaceObject(T obj, HashSet<object> visited, Func<string, string> replacer)
        {
            if (obj == null)
                return;

            if (!visited.Add(obj))
                return;
            
            _replacer(obj, visited, replacer);
        }

        public static void ReplaceObjects(IEnumerable<T> objects, HashSet<object> visited, Func<string, string> replacer)
        {
            if (objects == null)
                return;

            if (!visited.Add(objects))
                return;

            foreach (var obj in objects)
            {
                ReplaceObject(obj, visited, replacer);   
            }
        }
    }

    public static void ReplaceStrings<T>(T obj, Func<string, string> replacer)
    {
        ReplaceObject(obj, new HashSet<object>(ObjectReferenceEqualityComparer<object>.Default), replacer);
    }
}
1 使用正则表达式修改对象字符串属性的集合

我有一个Item对象的集合。 每个项目都有描述。 我想遍历集合,对于每个描述属性,我想将“deliver”替换为“send”。 每个描述中都会出现“传递”一词。 例如: 第1项:我们将在3天内交货。 第2项:我们将在2天内交付。 第3项:我们将在7天内交付。 ...

2 使用正则表达式修改字符串

我有一个看起来像这样的字符串: 所以我想获取b和d ( tmp的url属性),并更改String,使其看起来像这样( ex()函数返回String): 我需要使用正则表达式,因为没有现有的库可以解析这种类型的标记。 希望我的解释清楚。 提前致谢! ...

3 使用表达式动态评估属性字符串

如何构建一个能够实现以下目标的表达式: public object Eval(object rootObj, string propertyString) 例如: Eval(person, "Address.ZipCode") =&gt; return person.Address. ...

2013-05-14 13:34:34 2 1387   c#/ lambda
4 带有链式字符串方法的表达式树

我创建了一个看起来像这样的谓词: 我有以下代码: 但我想将谓词更改为: 我空白了。 我知道我必须在定义MethodInfo的地方添加一些东西。 有没有人有建议? ...

2015-05-15 17:31:41 3 717   c#/ lambda
5 字符串格式的表达式树? [重复]

可能重复: 在C#中序列化和反序列化表达式树 我最近涉足表达树这个野生世界,对此感到很好奇。 我也一直在玩ZeroMQ,这发生在我身上,我想知道是否可以将字符串形式的表达式树从客户端发送到服务器。 这可能吗? 表达式树似乎具有获取表达式的功能,并且看起来您应该能够将它们 ...

6 如何使用泛型和字符串进行表达式树“连接”具有内部和外部表达式

为什么我需要它: 我正在处理的程序有一个通用的查询构造函数,它使用 expression.trees 动态查询数据库的所有表。 通过简单的选择和几百或几千个数据,它工作正常。 当用户需要相关的表数据时,它使用延迟加载和映射来实现所需的内容,但是如果(以及何时)数据将达到数以千计,它将无法使用。 我 ...

7 正则表达式修改连接字符串

可以说我在C#中有此SQL Server连接字符串: 我想删除字符串的Driver =部分,无论它出现在字符串的何处。 这样一来,它看起来像这样: 我已经尝试正则表达式一段时间了,但是没有运气。 ...

2014-01-19 14:11:18 5 436   c#/ regex
8 正则表达式来修改字符串

我有一个需要修改的字符串。 我需要修改的部分是字符串的结尾。 它类似于“到 2017 年 5 月 23 日”。 我只需要更改它的存储日期。 所以我尝试了这样的事情: 但是,正则表达式有问题,因为我没有匹配日期。 我使用“to”是因为字符串中有另一个日期,但不需要修改这个日期。 有任何想法吗? ...

9 修改表达式以仅匹配绝对字符串 [重复]

这个问题在这里已经有了答案: Python 正则表达式匹配整个单词4答案 使用动态正则表达式匹配字符串中的整个单词1 个答案去年关闭。 我在下面有这个短语和字典列表: 下面的代码给出了想要的结果: 但是,“leed”不应该与第 3 个 url 匹配。 由于流血而匹配。 我只想要绝对匹配。 ...

10 使用正则表达式将字符串中的序列替换为已修改的字符

我正在尝试使用正则表达式来解决编码桶问题,无论它是否在网站上运行。 到目前为止,我有以下代码,它们不会在两个连续的相等字符之间添加* 。 相反,它只是推翻它们并用一个字符串替换它们。 我想知道如何继续使用正则表达式并替换整个字符串。 如果需要,我认为递归系统可以提供帮助。 ...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2022 STACKOOM.COM