C#/Salesforce: Must Constrain Generic, Cannot Constrain Generic
This question is equal parts C# and Salesforce, there are probably solutions possible from either side. Suggestions welcome!
I'm writing a generic class to read Salesforce data. The signature looks like this:
public abstract class SalesforceReader<SalesforceObjectType, RecordType>
where SalesforceObjectType: sObject
This lets me use this code later on:
List<RecordType> records = new List<RecordType>();
QueryResult queryResult = service.query(query);
foreach (sObject rawRecord in queryResult.records)
records.Add(ConvertRecord((SalesforceObjectType)rawRecord));
...
public abstract RecordType ConvertRecord(SalesforceObjectType record);
The plan is to write implementations of this class which know how to parse, for example, a Salesforce Lead
object into a RecordType
, which may be a basic object[]
, a Dictionary<string, value>
, or a fully-defined struct as I choose later on.
So far, I'm all kinds of pleased with my brilliantly elegant solution. My Codey award is as good as won. Then I try to write an implementation. This definition is no good:
class LeadReader: SalesforceReader<Lead, object[]>
The compiler result is:
The type 'SalesforceExtractor.Salesforce.Lead' cannot be used as type
parameter 'SalesforceObjectType' in the generic type or method
'SalesforceUtilities.SalesforceReader<SalesforceObjectType,RecordType>'.
There is no implicit reference conversion from
'SalesforceExtractor.Salesforce.Lead' to
'SalesforceUtilities.Salesforce.sObject'.
Bummer. I have to have the where SalesforceObjectType: sObject
constraint in the abstract class so I can cast sObjects, but because the conversion is not implicit, it's not good enough for the implementing 开发者_JAVA百科class.
Do I need to kiss my neat little solution goodbye, or is there a way to salvage this? This is my first Salesforce project, so if I need to approach things differently, please let me know.
For the bad movie/MST3K buffs out there:
Where do "must" and "cannot" meet on the graph?
Aha, I just needed to walk away for half an hour and look at it again. After 20 years working with computers, you'd think I'd have learned that the problem is usually one of perspective.
Lead does inherit from sObject, but the abstract class was in a library, in a different namespace and project from the implementing class, and each of them was using the Salesforce WSDL. I was asking the compiler to cast SalesforceExtractor.Salesforce.Lead to SalesforceUtilities.Salesforce.sObject, which is not valid. I just had to be more explicit in my implementing class:
class LeadReader: SalesforceReader<SalesforceUtilities.Salesforce.Lead, object[]>
This compiles, and I think I should be good to go.
It sounds like you need to modify the Lead class to inherit from sObject. If those classes are not yours, you need to change your design.
The SF Lead
object does inherit from sObject
, so this is a job for generic type variance, a subset of covariance/contravariance. Good luck with your Codey acceptance speech.
精彩评论