簡體   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