在泛型中继承具有所需构造函数的类

时间:2015-03-11 22:06:08

标签: c# oop generics repository-pattern

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

#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

我创建了一个Generic Class来尝试使Property处理更清洁,但遇到了障碍。属性类看起来像:

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; }

但是,如果您在原始公共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
}

2 个答案:

答案 0 :(得分:0)

有一种使用反射的解决方案,尽管它可能不是最干净的OOP。 System.Runtime命名空间FormatterServices.GetUninitializedObject()中的FormatterService类的方法将在不调用构造函数的情况下创建实例。这个问题有一个类似的答案here

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

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

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

以下是完整代码:

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中尝试代码并查看结果。

希望它有所帮助。

答案 1 :(得分:0)

可以尝试类似:

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);
    }
}