在泛型中使用必需的构造函数继承 class

Inheriting class with required constructor in generics

我的解决方案中有几个对象使用以下模式:

#region Repositories
private RepositoryAccount _account;
private RepositoryInquiry _inquiry;
private RepositoryLoan _loan;

public RepositoryAccount Account { get { return _account ?? (_account = new RepositoryAccount(this)); } }
public RepositoryInquiry Inquiry { get { return _inquiry ?? (_inquiry = new RepositoryInquiry(this)); } }
public RepositoryLoan Loan { get { return _loan ?? (_loan = new RepositoryLoan(this)); } }
#endregion

我创建了一个通用 Class 来尝试使 属性 处理更干净一些,但 运行 遇到了麻烦。 属性 Class 看起来像:

public class Property<T> where T : class, new()
{
    private T _value;

    public T Value
    {
        get { return _value ?? (_value = new T()); }
        set
        {
            // insert desired logic here
            _value = value;
        }
    }

    public static implicit operator T(Property<T> value)
    {
        return value.Value;
    }

    public static implicit operator Property<T>(T value)
    {
        return new Property<T> { Value = value };
    }
}

我用新的通用 属性 替换了我的 属性(带有支持对象),例如:

public Property<RepositoryAccount> Account { get; set; }
public Property<RepositoryInquiry> Inquiry { get; set; }
public Property<RepositoryLoan> Loan { get; set; }

但是,如果您在原文中注意到 public getter,我需要用另一个对象实例化每个对象:这是必需的,并且这些存储库对象没有无参数构造函数。

有什么建议吗?


整个对象看起来像:

public class UnitOfWorkSymitar : IUnitOfWork
{
    #region Properties
    internal IPHostEntry IpHostInfo;
    internal IPAddress IpAddress;
    internal IPEndPoint RemotEndPoint;
    internal System.Net.Sockets.Socket Sender;
    internal byte[] Bytes = new byte[1024];
    internal bool IsAllowedToRun = false;
    internal string SymServerIp;
    internal int SymPort;
    public string State { get; set; }
    #endregion

    #region Constructor
    public UnitOfWorkSymitar() { OpenSymitarConnection(); }
    protected UnitOfWorkSymitar(string serverIp, int? port) { OpenSymitarConnection(serverIp, port); }
    #endregion

    #region Implement IUnitOfWork
    public void Commit() { /* No commit on this socket connection */ }
    public void Rollback() { /* No rollback on this socket connection */ }
    #endregion

    #region Implement IDisposable
    /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
    public void Dispose()
    {
        CloseSymitarConnection();
    }
    #endregion

    #region Private Helpers
    /// <summary>
    /// Connect to a remote device.
    /// </summary>
    /// <returns>Success/failure message</returns>
    private string OpenSymitarConnection(string serverIp = null, int? port = null)
    {
        var config = ConfigInfoSymitar.Instance;
        SymServerIp = serverIp ?? config.SymServerIp;
        SymPort = port ?? config.SymPort;
        try
        {
            // Establish the remote endpoint for the socket.
            //IpHostInfo = Dns.GetHostEntry(SymServerIp);
            //IpAddress = IpHostInfo.AddressList[0];

            IpAddress = Dns.GetHostAddresses(SymServerIp)[0];

            RemotEndPoint = new IPEndPoint(IpAddress, SymPort);
            // Create a TCP/IP socket.
            Sender = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            // Connect the socket to the remote endpoint. Catch any errors.
            Sender.Connect(RemotEndPoint);
        }
        catch (Exception ex)
        {
            State = DetermineError(ex);
            return State;
            //throw;
        }

        IsAllowedToRun = true;
        State = "Success";
        return State;
    }
    /// <summary>
    /// Setup and send the request
    /// </summary>
    /// <param name="request"></param>
    /// <returns></returns>
    public string SendMessage(string request)
    {
        try
        {
            // Encode the data string into a byte array and add the carriage return for SymConnect.
            var msg = Encoding.ASCII.GetBytes(request);
            if(!request.EndsWith("\n"))
                msg = Encoding.ASCII.GetBytes(request + "\n");
            // Send the data through the socket.
            var bytesSent = Sender.Send(msg);
            // Receive the response from the remote device.
            var bytesRec = Sender.Receive(Bytes);
            var response = Encoding.ASCII.GetString(Bytes, 0, bytesRec);
            response = response.Replace("\n", "");
            return response;
        }
        catch (Exception ex)
        {
            return DetermineError(ex);
        }
    }
    /// <summary>
    /// Close the connection to a remote device.
    /// </summary>
    /// <returns></returns>
    private string CloseSymitarConnection()
    {
        try
        {
            // Release the socket.
            Sender.Shutdown(SocketShutdown.Both);
            Sender.Close();
        }
        catch (Exception ex)
        {
            return DetermineError(ex);
        }
        finally
        {
            IsAllowedToRun = false;
        }
        return "Success!";
    }
    /// <summary>
    /// Determine if this is an Arguments, Socket or some other exception
    /// </summary>
    /// <param name="ex">The exception to be checked</param>
    /// <returns>String message</returns>
    private static string DetermineError(Exception ex)
    {
        if (ex.GetType() == typeof(ArgumentNullException))
            return "Missing Arguments: " + Environment.NewLine + ex.GetFullMessage();
        if (ex.GetType() == typeof(SocketException))
            return "Socket Error: " + Environment.NewLine + ex.GetFullMessage();
        return "Unexpected Error: " + Environment.NewLine + ex.GetFullMessage();
    }
    #endregion

    #region Symitar Samples
    private static string ExecSymConnectRequest(string symServerIP, int symPort, string request)
    {
        // Data buffer for incoming data.
        var bytes = new byte[1024];
        // Connect to a remote device.
        try
        {
            // Establish the remote endpoint for the socket.
            // This example uses port 11000 on the local computer.
            var ipHostInfo = Dns.GetHostEntry(symServerIP);
            var ipAddress = ipHostInfo.AddressList[0];
            var remoteEP = new IPEndPoint(ipAddress, symPort);
            // Create a TCP/IP socket.
            var sender = new System.Net.Sockets.Socket(AddressFamily.InterNetwork
                                  , SocketType.Stream
                                  , ProtocolType.Tcp);

            // Connect the socket to the remote endpoint. Catch any errors.
            try
            {
                sender.Connect(remoteEP);
                // Encode the data string into a byte array and add the carriage return for SymConnect.
                var msg = Encoding.ASCII.GetBytes(request + "\n");
                // Send the data through the socket.
                var bytesSent = sender.Send(msg);
                // Receive the response from the remote device.
                var bytesRec = sender.Receive(bytes);
                var response = Encoding.ASCII.GetString(bytes, 0, bytesRec);
                // Release the socket.
                sender.Shutdown(SocketShutdown.Both);
                sender.Close();
                response.Replace("\n", "");
                return response;
            }
            catch (ArgumentNullException ane)
            {
                return "Missing Arguments: " + ane.Message;
            }
            catch (SocketException se)
            {
                return "Socket Error: " + se.Message;
            }
            catch (Exception e)
            {
                return "Unexpected Error: " + e.Message;
            }
        }
        catch (Exception e)
        {
            return e.Message;
        }
    }
    #endregion

    #region Repositories
    private RepositoryAccount _account;
    private RepositoryInquiry _inquiry;
    private RepositoryLoan _loan;

    public RepositoryAccount Account { get { return _account ?? (_account = new RepositoryAccount(this)); } }
    public RepositoryInquiry Inquiry { get { return _inquiry ?? (_inquiry = new RepositoryInquiry(this)); } }
    public RepositoryLoan Loan { get { return _loan ?? (_loan = new RepositoryLoan(this)); } }
    #endregion
}

有一种使用反射的解决方案,尽管它可能不是最干净的 OOP 解决方案。 System.Runtime 命名空间 FormatterServices.GetUninitializedObject() will create an instance without calling the constructor. There is a similar answer to this problem here.

中 FormatterService class 的方法

为了使其适用于您的解决方案,您必须对代码进行一些更改。首先从你的泛型 class Property 声明中删除 new() 否则如果你尝试使用没有无参数构造函数的类型 T ,你总是会从编译器中得到一个错误。其次将此方法添加到您的 Property class:

private T GetInstance()
{
    return (T)FormatterServices.GetUninitializedObject(typeof(T)); //does not call ctor
}

它将return一个单元化的对象,但不会调用构造函数。

完整代码如下:

public class Property<T> where T : class
{
    private T _value;

    public T Value
    {
        get 
        {
            return _value ?? (_value = GetInstance()); 
        }
        set
        {
            // insert desired logic here
            _value = value;
        }
    }

    private T GetInstance()
    {
        return (T)FormatterServices.GetUninitializedObject(typeof(T)); //does not call ctor
    }

    public static implicit operator T(Property<T> value)
    {
        return value.Value;
    }

    public static implicit operator Property<T>(T value)
    {
        return new Property<T> { Value = value };
    }
}

我创建了一个gist here,你可以试试把代码放到LinqPad中看看结果。

希望对您有所帮助。

可能会尝试类似的方法:

class Program {
    Property<string> Foo = new Property<string>(() => "FOO!");
}

public class Property<T> where T : class {
    private Lazy<T> instance;

    public Property(Func<T> generator) {
        instance = new Lazy<T>(generator);
    }

    public T Value {
        get { return instance.Value; }
    }

    public static implicit operator T(Property<T> value) {
        return value.Value;
    }

    public static implicit operator Property<T>(T value) {
        return new Property<T>(() => value);
    }
}