+5 for the question: I think this a very interesting question, and the discussions in the responses fascinating.
First, as noted here by Sascha L.: if the use of 'Get in 'MyClass
absolutely depends on 'OtherClass being a ready-to-go instance, then: test for that immediately in the 'Get method, and throw an error if it's not usable !
The code I show here takes that one step further by making the 'GetBytes method (my name for your 'Get method) the only public
method that can be called on the class. Note that to do that, assuming 'DoStuff, and 'ConvertToFormat are defined in IService, you'll need to use
explicit interface implementation so those methods are hidden from external consumers of your code ... unless they cast their instance to an IService, of course.
I'd like to add my own ideas here, but, first, I'd like to ask you:
1. 'MyClass, instance of 'IService, contains a reference to an instance of 'IService named 'OtherClass, and you invoke its method 'DoStuff ... but, your instance 'MyClass defines no 'DoStuff method:
Therefore, your IService interface does not declare 'DoStuff, since, if it did, 'MyClass would also have to define 'DoStuff.
But, that's
not possible because you call 'DoStuff on an instance of IService.
The simplest explanation for this apparent contradiction would be that you simply omitted defining 'DoStuff in the code for 'MyClass that you show here ... if that's correct, then why not edit your original post, and show the full code for MyClass, and show the IService interface code.
Now, let me see if I can add something to this thread by a code example (that will compile, and run as is but is not fully tested:
using System;
using System.Collections.Generic;
using System.Linq;
namespace QuestForNice
{
public enum ServiceStates
{
Undefined,
Valid,
DataEmpty,
OtherServiceUnusable,
DataAccessError,
DataTransformError
}
public interface IService
{
ServiceStates ServiceState { get; }
IEnumerable<byte> Data { get; }
IService OtherService { get; }
ServiceStates GetBytes(ref byte[] bytes);
IEnumerable<byte> DoStuff(ref IEnumerable<byte> bytes);
IEnumerable<byte> ConvertToDifferentFormat(ref IEnumerable<byte> bytes);
}
public class Service : IService
{
private ServiceStates _serviceState;
public ServiceStates ServiceState {
get { return _serviceState; }
}
private IEnumerable<byte> _data;
public IEnumerable<byte> Data{
get { return _data; }
}
private IService _otherService;
public IService OtherService{
get { return _otherService; }
}
public Service(Service otherservice = null)
{
_serviceState = ServiceStates.Undefined;
if (otherservice != null)
{
_otherService = otherservice;
_serviceState = ServiceStates.Valid;
}
else
{
_serviceState = ServiceStates.OtherServiceUnusable;
}
}
public ServiceStates GetBytes(ref byte[] bytes)
{
if (bytes.Length == 0) return ServiceStates.DataEmpty;
if (_serviceState == ServiceStates.OtherServiceUnusable)
{
return ServiceStates.OtherServiceUnusable;
}
IEnumerable<byte> _data = bytes as IEnumerable<byte>;
try
{
_data = OtherService.DoStuff(ref _data);
}
catch (Exception)
{
{
return ServiceStates.DataAccessError;
}
}
if (_data == null)
{
return _serviceState = ServiceStates.DataEmpty;
}
try
{
_data = (this as IService).ConvertToDifferentFormat(ref _data);
}
catch (Exception)
{
{
return ServiceStates.DataTransformError;
}
}
if (_data == null)
{
return ServiceStates.DataEmpty;
}
bytes = _data.ToArray();
return ServiceStates.Valid;
}
IEnumerable<byte> IService.DoStuff(ref IEnumerable<byte> bytes)
{
return bytes.Select((byt, ndx) => (ndx % 2 == 0) ? (byte)((char)(byt + 32)) : byt);
}
IEnumerable<byte> IService.ConvertToDifferentFormat(ref IEnumerable<byte> bytes)
{
return bytes;
}
}
}
Here's a sample test-case:
using System;
using System.Collections.Generic;
using System.Text;
using QuestForNice;
private Service Service1;
private Service Service2;
private byte[] servicetest;
private void TestQuestForNice(object sender, EventArgs e)
{
Service2 = new Service();
Service1 = new Service(Service2);
servicetest = Encoding.ASCII.GetBytes("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
ServiceStates testresult = Service1.GetBytes(ref servicetest);
Console.Write("test complete, status: {0}", testresult);
if (testresult == ServiceStates.Valid)
{
string result = System.Text.Encoding.Default.GetString(servicetest);
Console.WriteLine(" result value: {0}", result);
}
}
The outputL:
test complete, status: Valid result value: aBcDeFgHiJkLmNoPqRsTuVwXyZ
Notes:
0. remember that the code shown here, the transform function, etc. is meant to serve only as an example of a certain strategy, and, hopefully, another p.o.v. in the discussion, here; you should never take code like this and put it into production without extensive analysis, testing, etc.
1. the degree (the strength) to which you try to implement systematic error-trapping, and detailed result logging, as shown here, should reflect a reasonable analysis of how you might expect current and future consumers of your code will expect, want, wish for, your code to "behave." Don't put on a suit of armor if your opponents are always mere ants :)
2. in this example, the test data-transform function is
hard-wired into the 'DoStuff method; I doubt that's what you will really want, and, I'd re-work this to make that method a delegate that can be set at run-time by a consumer of the code (or fallback on a default implementation, if it is not set, externally).
3. finally, I anticipate re-writing some other code I am using now, based on some ideas in this code experiment, and, will revise this code if I have have more ideas. On first "smell," I think this code may be a little too clever.
4. note the rather elaborate lengths gone to here ... making the interface properties implementation 'readonly ('get only) ... to prevent the external user from setting them. Over-kill ? Maybe, depend on context.
5. To my mind, your use of the function name 'ConvertToDifferentFormat implies some kind of possible type-conversion, and that makes me think that you will need to use generics, here.