Повторное использование вложенного служебного класса - PullRequest
0 голосов
/ 11 декабря 2019

Я проектировал класс, в котором я хотел, чтобы свойства были доступны в полной форме внутри класса, но только в очищенной форме снаружи. Моим первым решением было реализовать два разных get элемента с разными правами доступа:

public class MyClass
{   
   private Foo CompleteFoo { get; set; }
   public Foo SanitizedFoo => CompleteFoo.WithoutSensitiveInfo();
}

. По мере роста моего класса у него появилось много таких свойств, а также необходимость определять эти методы доступа для каждого значения. стал утомительным и сделал класс труднее читать. Поэтому я решил ввести вложенный вспомогательный класс:

public class MyClass
{
    private class SanitizedValue<T> where T : Sanitizable 
    {
        private T Complete { get; set; }
        public T Sanitized => T.WithoutSensitiveInfo();
    }

    public SanitizedValue<Foo> foo { get; }
} 

Теперь сам MyClass имеет доступ к значениям Sanitized и Complete, но внешние пользователи имеют доступ только к значению Sanitized.

Проблема в том, что я считаю этот класс SanitizedValue довольно полезной утилитой, которую я, возможно, захочу использовать в различных классах для репликации шаблона прав доступа. Но поскольку SanitizedValue должен быть внутренним по отношению к MyClass, чтобы правильно разрешить доступ к свойству Complete, кажется, что мне придется переписать класс SanitizedValue для каждого родителя, который захочет его использовать.

Есть ли стратегия, позволяющая избежать такого дублирования внутреннего класса?

Ответы [ 2 ]

0 голосов
/ 11 декабря 2019

Вы пытаетесь ограничить доступ через аксессор вместо использования контракта (интерфейса). Намного проще ограничить доступ к свойствам класса, методам и полям с помощью интерфейса.

В этом примере я не только ограничил Person с помощью IPerson, но также ограничил Person.SSN, выставив только ISensitiveStringне SensitiveString

DotNetFiddle Пример

* *
namespace One
{
    using System;

    public class Program
    {
        public static void Main()
        {
            var person = Two.PersonFactory.Create("123-45-6789");
            Console.WriteLine(person.SSN.Value);
            // Console.WriteLine(person.SSN.RawValue); // 'ISensitiveString' does not contain a definition for 'RawValue' and no accessible extension method 'RawValue' accepting a first argument of type 'ISensitiveString' could be found
        }
    }
}

namespace Two
{
    using System;
    using System.Security.Cryptography;

    public static class PersonFactory
    {
        public static IPerson Create(string SSN)
        {
            var result = new Person();
            result.SSN.RawValue = SSN;

            Console.WriteLine(result.SSN.RawValue);
            Console.WriteLine(result.SSN.Value);

            return result;
        }
    }

    public interface IPerson 
    {
        ISensitiveString SSN { get; }
    }

    internal class Person : IPerson
    {
        ISensitiveString IPerson.SSN => SSN;

        public SensitiveString SSN { get; set; } = new SensitiveString();
    }

    public interface ISensitiveString
    {
        string Value { get; }
    }

    internal class SensitiveString : ISensitiveString
    {
        public string Value
        {
            get
            {
                string result;
                using (var sha512 = SHA512.Create())
                {
                    var bytes = System.Text.Encoding.UTF8.GetBytes(RawValue);
                    var hash = sha512.ComputeHash(bytes);
                    var sb = new System.Text.StringBuilder(128);
                    foreach (var b in hash)
                        sb.Append(b.ToString("X2"));
                    result = sb.ToString();
                }
                return result;
            }
        }

        public string RawValue { get; set; }
    }
}
1014 * 1015 Ouput * * *
1017 * 123-45-6789 FBE47783B1D59D46DD437BDE9236A5404A8E8394F76E1C31D8421FD604D9BD1DF0C52DF5E5FD9C41879659D46C9A839266144E6C5FCAA678653290ED9EFFAEA7 +1018 *

FBE47783B1D59D46DD437BDE9236A5404A8E8394F76E1C31D8421FD604D9BD1DF0C52DF5E5FD9C41879659D46C9A839266144E6C5FCAA678653290ED9EFFAEA7

0 голосов
/ 11 декабря 2019

Если то, что вы пытаетесь достичь, - это сузить общедоступный API только до тех данных, которые должны быть доступны извне, как насчет введения публичного интерфейса, который используется в сигнатуре свойства?

Например,:

public class FooContainer
{
   private Foo _myFoo;
   public IFoo MyFoo => _myFoo;
}

public class Foo : IFoo
{
   //This is visible in your public API
   public bool NonSensitiveFlag { get; private set; }
   public string NonSensitiveString { get; private set; }

   //This is not visible due to not being present in IFoo definition
   public string MySensitiveString { get; set; }
}

public interface IFoo
{
   bool NonSensitiveFlag { get; }
   string NonSensitiveString { get; }
}

Это, конечно, только каким-то образом решает проблему, возникшую у вас в начале, но не проблему с самим SanitizedValue<T>.

...