0

I have several objects in my solution that use the following 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

I created a Generic Class to try to make the Property handling a little cleaner but ran into a snag. The Property Class looks like:

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

I replace my property (with backing objects) with the new Generic Property like:

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

However, if you noticed in the original public getter, I need to instantiate each object with another object: this is required and there is no paramaterless constructor for these repository objects.

Any suggestions?


The whole object looks like:

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
}
codingadventures
  • 2,924
  • 2
  • 19
  • 36
Keith Barrows
  • 24,802
  • 26
  • 88
  • 134
  • Do you have ownership over the repository classes? Could you give them a parameterless constructor and then append other required attributes? – Nick Bailey Mar 11 '15 at 22:16
  • Only if I create the Property object, initialize it, etc. I won't be saving myself anything by the time I finish. And the Repositories utilize a base class which enforce a parameterized constructor. It would be a pretty big effort to redo all my data access layer code. – Keith Barrows Mar 11 '15 at 22:26

2 Answers2

0

There is a solution using reflection although it might not be the cleanest OOP one. A method of the FormatterService class in the System.Runtime namespace FormatterServices.GetUninitializedObject() will create an instance without calling the constructor. There is a similar answer to this problem here.

In order to make it work with your solution you have to make some changes to your code. First of all remove the new() from your generic class Property declaration otherwise you will always get an error from the compiler if you try to use a type T with no parameter-less constructor. Second add this method to your Property class:

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

It will return an unitialized object but it won't call the constructor.

Here is the full code:

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

I created a gist here, you can try the code into LinqPad and see the result.

hope it helps.

Community
  • 1
  • 1
codingadventures
  • 2,924
  • 2
  • 19
  • 36
0

Might try something like:

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);
    }
}
Ryan CS
  • 128
  • 1
  • 5