简体   繁体   中英

Base class, derived class, and Iclass?

I am trying to figure out how this code that one of the more advanced developers wrote works, and I am thoroughly confused. I was hoping that someone can help me make sense of all the different classes and interfaces present in this project.

I have an outside class is present in a class library dll, separate from a web application that I am editing. Let's call it outsideClass. I cannot figure out why there are so many constructors for this class, and how the requests are even made because only one constructor has a default WebRequestHandler and the rest do not. In the web application, the instances of this class get initialized with the WebRequestHandler being null!! This is driving me crazy...here is the outsideClass:

public class outsideClass : webServiceInterface
{
    private HttpClient _client = null;
    private const int _TIMEOUT = 50;
    private const string _assemblyName = "my.assembly.name";
    private const string _resourcesName = "my.assembly.name.Properties.Resources";
    private const string _certName = "client_cert_name";

    private static WebRequestHandler CreateDefaultMessageHandler()
    {
        // ProgressMessageHandler prog = new ProgressMessageHandler();
        //HttpClientHandler handler = new HttpClientHandler();

        WebRequestHandler handler = new WebRequestHandler();

        // The existing HttpClientHandler.SupportsProxy property indicates whether both the UseProxy property and the Proxy property are supported.
        // This created an issue because some platforms(for example, Windows Phone) don’t allow you to configure a proxy explicitly.
        // However, Windows Phone lets you control whether the machine wide proxy should be used.
        // To model this behavior, we added the HttpClientHandler.SupportsUseProxy() extension method.
        // For some platforms that don’t support both, such as Windows Phone, the SupportsProxy property will continue to return false, but the SupportsUseProxy() method will return true.
        if (handler.SupportsProxy)  // false on WinPhone
        {
            handler.Proxy = CrossWebProxy.SystemWebProxy;
            handler.UseProxy = true;
        }

        // HttpClientHandler.SupportsAllowAutoRedirect(): The HttpClientHandler.SupportsRedirectConfiguration property had a similar issue: 
        // It controls whether both the AllowAutoRedirect and the MaxAutomaticRedirections properties are supported. 
        // Windows Phone doesn’t support specifying the maximum number of automatic redirections, but it does support redirection.
        // For that reason, we added the HttpClientHandler.SupportsAllowAutoRedirect() extension method.
        /*
        if (handler.SupportsRedirectConfiguration)      // false on WinPhone
        {
            handler.MaxAutomaticRedirections = 5;
        } */
        if (handler.SupportsRedirectConfiguration)
        {
            handler.AllowAutoRedirect = true;
        }

        X509Certificate2 certificate = getClientCertificate();
        handler.ClientCertificates.Add(certificate);

        return handler;
    }

    private static X509Certificate2 getClientCertificate()
    {
        System.Reflection.Assembly myAssembly;
        myAssembly = System.Reflection.Assembly.Load(_assemblyName);
        ResourceManager myManager = new ResourceManager(_resourcesName, myAssembly);

        byte[] cert_file_bytes;
        cert_file_bytes = (byte[])myManager.GetObject(_certName);

        //string[] names = myAssembly.GetManifestResourceNames();
        //using (Stream cert_file_stream = myAssembly.GetManifestResourceStream(names[0]))
        //using (var streamReader = new MemoryStream())
        //{
        //    cert_file_stream.CopyTo(streamReader);
        //    cert_file_bytes = streamReader.ToArray();
        //}

        X509Certificate2 cert_file = new X509Certificate2(cert_file_bytes, "server");
        return cert_file;
    }

    public outsideClass(params DelegatingHandler[] handlers) : this(null, handlers)
    {
    }

    public outsideClass(WebRequestHandler innerHandler, params DelegatingHandler[] handlers) : this("", "", innerHandler, handlers)
    {
    }

    public outsideClass(string aUser, string aPass, params DelegatingHandler[] handlers) : this(aUser, aPass, null, null, handlers)
    {
    }

    public outsideClass(string aUser, string aPass, Uri aBaseAddress, params DelegatingHandler[] handlers) : this(aUser, aPass, null, null, handlers)
    {
    }

    public outsideClass(string aUser, string aPass, WebRequestHandler innerHandler, params DelegatingHandler[] handlers) : this(aUser, aPass, null, innerHandler, handlers)
    {
    }

    public outsideClass(string aUser, string aPass, Uri aBaseAddress, WebRequestHandler innerHandler, params DelegatingHandler[] handlers)
    {
        if (innerHandler == null)
        {
            innerHandler = CreateDefaultMessageHandler();
        }

        if (handlers != null)
        {
            DelegatingHandler excHandler = handlers.FirstOrDefault(t => t is WebServiceExceptionsHandler);
            if (excHandler == null)
            {
                // Add the custom handler to the list
                // So this Client will raise only exception of type WebServiceException / WebServiceOfflineException / WebServiceTimedOutException / WebServiceStatusException
                excHandler = new WebServiceExceptionsHandler();
                IList<DelegatingHandler> list = handlers.ToList();
                list.Insert(0, excHandler);
                handlers = list.ToArray();
            }
        }

        _client = HttpClientFactory.Create(innerHandler, handlers);
        Timeout = TimeSpan.FromSeconds(_TIMEOUT);
        SetCredentials(aUser, aPass);

        BaseAddress = aBaseAddress;
    }

    protected long MaxResponseContentBufferSize
    {
        get { return _client.MaxResponseContentBufferSize; }
        set { _client.MaxResponseContentBufferSize = value; }
    }

    protected HttpRequestHeaders DefaultRequestHeaders { get { return _client.DefaultRequestHeaders; } }

    public Uri BaseAddress
    {
        get { return _client.BaseAddress; }
        set { _client.BaseAddress = value; }
    }

    public TimeSpan Timeout
    {
        get { return _client.Timeout; }
        set { _client.Timeout = value; }
    }

    public string Id { get; private set; }
    public string Key { get; private set; }

    /// <summary>
    /// Set Basic Authentication Header
    /// </summary>
    public void SetCredentials(string aApplicationId, string aApplicationKey)
    {
        Id = aApplicationId;
        Key = aApplicationKey;
        _client.DefaultRequestHeaders.Authorization = null;

        if (string.IsNullOrEmpty(Id) == true ||
            string.IsNullOrEmpty(Key) == true)
        {
            return;
        }

        _client.DefaultRequestHeaders.Authorization =
            new AuthenticationHeaderValue("Basic",
            Convert.ToBase64String(System.Text.UTF8Encoding.UTF8.GetBytes(string.Format("{0}:{1}",
            this.Id, this.Key))));
    }

    protected async Task<T> SendAndReadAsAsync<T>(HttpRequestMessage aRequest, MediaTypeFormatterCollection formatters, CancellationToken aCancellationToken = default(CancellationToken))
    {
        HttpResponseMessage response = await _client.SendAsync(aRequest, aCancellationToken).ConfigureAwait(false);
        return await response.Content.ReadAsAsync<T>(formatters).ConfigureAwait(false);
    }

    protected async Task<T> SendAndReadAsAsync<T>(HttpRequestMessage aRequest, CancellationToken aCancellationToken = default(CancellationToken))
    {
        HttpResponseMessage response = await _client.SendAsync(aRequest, aCancellationToken).ConfigureAwait(false);
        return await response.Content.ReadAsAsync<T>().ConfigureAwait(false);
    }

    protected async Task<string> SendAndReadAsStringAsync(HttpRequestMessage aRequest, CancellationToken aCancellationToken = default(CancellationToken))
    {
        HttpResponseMessage response = await _client.SendAsync(aRequest, aCancellationToken).ConfigureAwait(false);
        return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
    }

    protected async Task<Stream> SendAndReadAsStreamAsync(HttpRequestMessage aRequest, HttpCompletionOption aCompletionOption = HttpCompletionOption.ResponseHeadersRead, CancellationToken aCancellationToken = default(CancellationToken))
    {
        HttpResponseMessage response = await _client.SendAsync(aRequest, aCompletionOption, aCancellationToken).ConfigureAwait(false);
        return await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
    }

    protected async Task<byte[]> SendAndReadAsByteArrayAsync(HttpRequestMessage aRequest, CancellationToken aCancellationToken = default(CancellationToken))
    {
        HttpResponseMessage response = await _client.SendAsync(aRequest, aCancellationToken).ConfigureAwait(false);
        return await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
    }

    protected async Task<HttpResponseMessage> SendAsync(HttpRequestMessage aRequest, CancellationToken aCancellationToken = default(CancellationToken))
    {
        HttpResponseMessage response = await _client.SendAsync(aRequest, aCancellationToken).ConfigureAwait(false);
        return response;
    }
}

The webServiceInterface (why is this needed and what is the purpose?):

public interface webServiceInterface
{
    Uri BaseAddress { get; set; }
    TimeSpan Timeout { get; set; }
    string Id { get; }
    string Key { get; }
    void SetCredentials(string aId, string aKey);
}

This is the code that is confusing the hell out of me, found in the web application, there is an IConnClient and a ConnClient class (why?) and they are defined like this:

public interface IConnClient : IClientWebService
{
    Task UpdateSomethingAsync(Guid aCompanyId, Guid aUserId, Guid aAgronomistId, bool shareCN1, bool getRxMap, DateTime endDate, CancellationToken aCancelToken = default(CancellationToken));
    Task<IList<SomethingDto>> GetSomethingListAsync(Guid aCompanyId, CancellationToken aCancelToken = default(CancellationToken));
    Task DeleteSomethingAsync(Guid aCompanyId, Guid aAgronomistId, CancellationToken aCancelToken = default(CancellationToken));
    Task<IList<StuffDto>> GetStuffListAsync(Guid aCompanyId, CancellationToken aCancelToken = default(CancellationToken));
    Task<IList<StuffDto>> GetStuffListAsync(Guid aCompanyId, Guid aAgronomistId, CancellationToken aCancelToken = default(CancellationToken));
    Task<IList<StuffDto>> GetStuffListForThingsAsync(Guid aCompanyId, Guid aThingsId, CancellationToken aCancelToken = default(CancellationToken));
    Task<IList<ThingsDetailsDto>> GetThingsListAsync(Guid aCompanyId, CancellationToken aCancelToken = default(CancellationToken));
    Task<string> SendStuffListToThingsListAsync(Guid aCompanyId, Guid aUserId, IList<StuffDto> aStuffList, IList<Guid> aThingsList, CancellationToken aCancelToken = default(CancellationToken));

    // Task<bool> GetStuffStatusAsync(Guid aCompanyId, string aStuffId, CancellationToken aCancelToken = default(CancellationToken));
}


public class ConnClient : outsideClass, IConnClient
{
    private const string _SHARE_CN1 = "Share-CN1";
    private const string _GET_RXMAP = "Get-RxMap";

    private DateTime Epoch2DateTime(long epoch)
    {
        return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(epoch);
    }

    private long DateTime2Epoch(DateTime datetime)
    {
        return (long)datetime.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
    }

    public ConnClient(Uri aBaseAddress, string apiKey, WebRequestHandler innerHandler = null, params DelegatingHandler[] handlers)
        : base("", "", innerHandler, handlers) // DIP - IoC
    {
        DefaultRequestHeaders.Authorization = null; // TODO: Add authorization?
        DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", apiKey);
        BaseAddress = aBaseAddress;
    }

    public async Task UpdateSomethingAsync(Guid aCompanyId, Guid aUserId, Guid aAgronomistId, bool shareCN1, bool getRxMap, DateTime endDate, CancellationToken aCancelToken = default(CancellationToken))
    {
        if (aCompanyId == Guid.Empty)
            throw new ArgumentException(nameof(UpdateSomethingAsync) + " Company GUID is Empty");
        if (aUserId == Guid.Empty)
            throw new ArgumentException(nameof(UpdateSomethingAsync) + " User GUID is Empty");
        List<string> permissions = new List<string>();
        if (shareCN1)
        {
            permissions.Add(_SHARE_CN1);
        }
        if (getRxMap)
        {
            permissions.Add(_GET_RXMAP);
        }
        UpdateSomethingRequestDto reqDto = new UpdateSomethingRequestDto();
        reqDto.Somethings = new Somethings();
        reqDto.Somethings.userId = aUserId;
        reqDto.Somethings.thirdparty = aAgronomistId;

        if (endDate.Year != 1)
            reqDto.Somethings.endDate = DateTime2Epoch(endDate);
        else
            reqDto.Somethings.endDate = 0;

        reqDto.Somethings.permissions = permissions;

        HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Put, "companies/" + aCompanyId + "/Somethings");

        JsonMediaTypeFormatter fmt = new JsonMediaTypeFormatter();
        request.Content = new ObjectContent(reqDto.GetType(), reqDto, fmt, "application/json");

        var res = await SendAndReadAsStringAsync(request, aCancelToken);
    }

    public async Task<IList<SomethingDto>> GetSomethingListAsync(Guid aCompanyId, CancellationToken aCancelToken = default(CancellationToken))
    {
        if (aCompanyId == Guid.Empty)
            throw new ArgumentException(nameof(GetSomethingListAsync) + " Company GUID is Empty");
        HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "companies/" + aCompanyId + "/Somethings");
        GetSomethingsResponseDto res = await SendAndReadAsAsync<GetSomethingsResponseDto>(request, aCancelToken);
        return res?.Somethings;
    }

    public async Task DeleteSomethingAsync(Guid aCompanyId, Guid aAgronomistId, CancellationToken aCancelToken = default(CancellationToken))
    {
        if (aCompanyId == Guid.Empty)
            throw new ArgumentException(nameof(DeleteSomethingAsync) + " Company GUID is Empty");
        if (aAgronomistId == Guid.Empty)
            throw new ArgumentException(nameof(DeleteSomethingAsync) + " Agronomist GUID is Empty");
        HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Delete, "companies/" + aCompanyId + "/Somethings?thirdPartyId=" + aAgronomistId);
        var res = await SendAndReadAsStringAsync(request, aCancelToken);
    }

    public async Task<IList<StuffDto>> GetStuffListAsync(Guid aCompanyId, CancellationToken aCancelToken = default(CancellationToken))
    {
        if (aCompanyId == Guid.Empty)
            throw new ArgumentException(nameof(GetStuffListAsync) + " Company GUID is Empty");
        HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "companies/" + aCompanyId + "/Stuffs");
        GetStuffsResponseDto res = await SendAndReadAsAsync<GetStuffsResponseDto>(request, aCancelToken);
        return res?.Stuffs;
    }

    public async Task<IList<StuffDto>> GetStuffListAsync(Guid aCompanyId, Guid aAgronomistId, CancellationToken aCancelToken = default(CancellationToken))
    {
        if (aCompanyId == Guid.Empty)
            throw new ArgumentException(nameof(GetStuffListAsync) + " Company GUID is Empty");
        if (aAgronomistId == Guid.Empty)
            return await GetStuffListAsync(aCompanyId);
        HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "companies/" + aCompanyId + "/Stuffs?thirdPartyId=" + aAgronomistId);
        GetStuffsResponseDto res = await SendAndReadAsAsync<GetStuffsResponseDto>(request, aCancelToken);
        return res?.Stuffs;
    }

    public async Task<IList<ThingsDetailsDto>> GetThingsListAsync(Guid aCompanyId, CancellationToken aCancelToken = default(CancellationToken))
    {
        if (aCompanyId == Guid.Empty)
            throw new ArgumentException(nameof(GetThingsListAsync) + " Company GUID is Empty");
        HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "vehicles/?companyId=" + aCompanyId);
        GetVehiclesResponseDto res = await SendAndReadAsAsync<GetVehiclesResponseDto>(request, aCancelToken);
        return res?.vehicles;
    }

    private SendStuffsRequestDto CreateSendStuffDto(Guid aUserId, IList<StuffDto> aStuffList, IList<Guid> aVehicleList)
    {
        SendStuffsRequestDto res = new SendStuffsRequestDto();
        res.StuffData = new List<StuffDataDto>();
        res.userId = aUserId;
        foreach (StuffDto prescr in aStuffList)
        {
            StuffDataDto data = new StuffDataDto();
            data.StuffID = prescr.StuffId;
            data.vehicles = aVehicleList;
            res.StuffData.Add(data);
        }
        return res;
        /*
        SendStuffsRequestDto res = new SendStuffsRequestDto();
        res.StuffData = new List<StuffDataDto>();
        res.userId = aUserId;
        foreach (StuffDto prescr in aStuffList)
        {
            IList<Guid> prescrVehicleList = prescr.vehicleDetails.Select(l => l.vehicleId).ToList();
            IList<Guid> joinedList = aVehicleList.Where(c => prescrVehicleList.Contains(c)).ToList();
            if (joinedList == null || joinedList.Count == 0)
                continue;

            StuffDataDto data = new StuffDataDto();
            data.StuffID = prescr.StuffId;
            data.vehicles = new List<Guid>();
            data.vehicles = joinedList;
            res.StuffData.Add(data);
        }
        return res;*/
    }

    public async Task<string> SendStuffListToThingsListAsync(Guid aCompanyId, Guid aUserId, IList<StuffDto> aStuffList, IList<Guid> aVehicleList, CancellationToken aCancelToken = default(CancellationToken))
    {
        if (aCompanyId == Guid.Empty)
            throw new ArgumentException(nameof(SendStuffListToThingsListAsync) + " Company GUID is Empty");
        if (aUserId == Guid.Empty)
            throw new ArgumentException(nameof(SendStuffListToThingsListAsync) + " User GUID is Empty");

        SendStuffsRequestDto reqDto = CreateSendStuffDto(aUserId, aStuffList, aVehicleList);
        if (reqDto.StuffData.Count == 0)
            return "";
        HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "companies/" + aCompanyId + "/Stuffs/send");

        JsonMediaTypeFormatter fmt = new JsonMediaTypeFormatter();
        request.Content = new ObjectContent(reqDto.GetType(), reqDto, fmt, "application/json");

        return await SendAndReadAsStringAsync(request, aCancelToken);
    }

    public async Task<IList<StuffDto>> GetStuffListForThingsAsync(Guid aCompanyId, Guid aVehicleId, CancellationToken aCancelToken = default(CancellationToken))
    {
        if (aCompanyId == Guid.Empty)
            throw new ArgumentException(nameof(GetStuffListForThingsAsync) + " Company GUID is Empty");
        if (aVehicleId == Guid.Empty)
            throw new ArgumentException(nameof(GetStuffListForThingsAsync) + " Vehicle GUID is Empty");

        IList<StuffDto> allPresc = await GetStuffListAsync(aCompanyId, aCancelToken);
        List<StuffDto> res = new List<StuffDto>();
        foreach (StuffDto prescr in allPresc)
        {
            ThingsDetailsDto vehicle = prescr.vehicleDetails.FirstOrDefault(t => t.vehicleId == aVehicleId);
            if (vehicle != null)
            {
                // Clones the Stuff...
                ISerializationInterface serializer = new SerializationJson();
                string tmpString = serializer.Serialize(prescr);
                StuffDto newPrescr = serializer.Deserialize<StuffDto>(tmpString);
                // Remove all the vehicles
                newPrescr.vehicleDetails.Clear();
                // Add the vehicle found
                newPrescr.vehicleDetails.Add(vehicle);
                res.Add(newPrescr);
            }
        }
        return res;
    }

    /*
    public Task<bool> GetStuffStatusAsync(Guid aCompanyId, string aStuffId, CancellationToken aCancelToken = default(CancellationToken))
    {
        // This API has been marked as Optional on PostMan...
        throw new NotImplementedException();
    } */
}

Finally, this is how it is used in the code:

IConnClient connClient = ConnClientFactory.Create();
connClient.Timeout = TimeSpan.FromMinutes(4);

//Then later in the code:
connClient.GetStuffListAsync(companyGuid);

The part that I am extremely confused about is this part in the connClient:

public ConnClient(Uri aBaseAddress, string apiKey, WebRequestHandler innerHandler = null, params DelegatingHandler[] handlers)
        : base("", "", innerHandler, handlers) // DIP - IoC

It looks like they are using a constructor where the innerHandler (WebRequestHandler) is not initialized with a default, and is therefore null, so how can it be making a web service call? Can anyone help explain the connection between all of these classes and interfaces? It is extremely overwhelming and hard to figure out what is happening here...

I think an explanation of contructor chaining will help your understanding quite a bit here.

public outsideClass(params DelegatingHandler[] handlers) : this(null, handlers) { }

This is the first constructor from outsideClass . Notice that it has no body, but more important is this part: this(null, handlers) . This part of the constructor says 'First, call my OTHER constructor that takes two parameters, then do some extra stuff'. In this case, however, there is no extra stuff.

So

public outsideClass(params DelegatingHandler[] handlers) : this(null, handlers) { }

calls

public outsideClass(WebRequestHandler innerHandler, params DelegatingHandler[] handlers) : this("", "", innerHandler, handlers) { }

which calls

public outsideClass(string aUser, string aPass, WebRequestHandler innerHandler, params DelegatingHandler[] handlers) : this(aUser, aPass, null, innerHandler, handlers) { }

which finally calls your 'main' constructor that has all your logic.

public outsideClass(string aUser, string aPass, Uri aBaseAddress, WebRequestHandler innerHandler, params DelegatingHandler[] handlers){ ... }

The reason you'd structure your constructors like this is to reduce code duplication, and make sure that if you have to change constructor logic, you successfully change them all.

Next, let's look at ConnClient . It's constructor looks like

public ConnClient(Uri aBaseAddress, string apiKey, WebRequestHandler innerHandler = null, params DelegatingHandler[] handlers)
        : base("", "", innerHandler, handlers) // DIP - IoC

Pretty similar to outsideClass , but instead of this(...) , it has base("", "", innerHandler, handlers) . base functions much like this , but instead of calling another constructor in the SAME class, it calls a constructor in the BASE class.

So,

public ConnClient(Uri aBaseAddress, string apiKey, WebRequestHandler innerHandler = null, params DelegatingHandler[] handlers)
        : base("", "", innerHandler, handlers) // DIP - IoC

calls

public outsideClass(string aUser, string aPass, WebRequestHandler innerHandler, params DelegatingHandler[] handlers) : this(aUser, aPass, null, innerHandler, handlers) { }

which again calls your 'main' constructor!

public outsideClass(string aUser, string aPass, Uri aBaseAddress, WebRequestHandler innerHandler, params DelegatingHandler[] handlers){ ... }

Lastly, to answer your question about Interfaces. There are a number of reasons to use them in code. Some of the biggest benefits is that they decouple any consumers of your class from the actual class. This means, that if you were to change implementation sometime in the future, you can save yourself from having to rework all of your consumers as well as the code itself.

Think of an example where you're writing a library to work with databases. You have a class MySqlDatabaseAdapater . If all your code uses MySqlDatabaseAdapter , and you decide to change to OracleDatabaseAdapter , you have to change everything using MySqlDatabaseAdapter ! But, if arrange your code with MySqlDatabaseAdapter implementing an interface, IDatabaseAdapter and all your consuming code references IDatabaseAdapter instead, now you can replace your consumers with OracleDatabaseAdapater , which ALSO implements IDatabaseAdapter and you have much less code to change!

Another benefit is for testing code. If you have a class that does network activity (which this kind of looks like) and want to unit test code that uses this class, then your unit tests will be doing network activity. Not good! BUT, if you use the interface approach, then you can setup your tests to give the code you're testing a 'fake' IConnClient that pretends to hit networks, but doesn't really. This lets you write solid, predicatable, and fast tests.

Hope this helps!

This isn't entirely an answer, but I can't put formatted code in a comment.

This is why outsideClass can work even though innerHandler is null in the constructor:

if (innerHandler == null)
{
    innerHandler = CreateDefaultMessageHandler();
}

Also, the comment at the end of this line might reveal something:

public ConnClient(Uri aBaseAddress, string apiKey, WebRequestHandler innerHandler = null, params DelegatingHandler[] handlers)
    : base("", "", innerHandler, handlers) // DIP - IoC

I suspect that DIP - IoC might be a reference to a dependency injection container (sometimes also called an "IoC container".) That means that the project might be using a DI container like Unity, Windsor, Autofac, or something similar. You might see a reference to one of those in the project. If so, that might explain the particular use of interfaces. The typical pattern is to

  • define an interface
  • define a class that implements the interface
  • make other classes dependent on the interface (not the class)
  • configure the DI container so that if a class depends on the interface, the container creates an instance of the class.

That's not a great explanation of dependency injection, but if that guess is right then it might help you find some code that indicates what values are supplied to all of these constructors.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM