|
Valid in current paradigm:
var a = 5.5;
Invalid in current paradigm, but valid if paradigm theoretically adopted by C#:
a = new func<double>(delegate() { return 5.5; });
Using a theoretical library of additional functions, both are valid:
DTBase<double> a = (DTVar<double>)5.5;
a = new DTFunc<double>(delegate() { return 5.5; });
The key point is that, instead of being a specific type of variable, "a" is now defined by what you get when you call it.
|
|
|
|
|
And here is a heavily-commented "Guess My Number" game using this paradigm (granted, it's not runnable without the classes, but it is heavily-commented. Should I release that code? I've no idea if anyone is the least bit intruiged):
DTVar<int> length = 7;
Console.WriteLine("Guess my Number, a {0}-digit number.", length);
DTVar<bool> allCorrect = false;
DTFunc<char, char, string> matchingF = new DTFunc<char, char, string>(
(a, b) =>
{
string r = "";
while (a.Count > 0)
{
char a1 = a;
char b1 = b.Call;
r += (a1 == b1 ? a1 : 'X');
}
return r;
}
);
DTBase<string> numberF = new DTFunc<string>(
delegate()
{
string r = "";
for (int t = 0; t < length; ++t) r += Rand.Next(10);
return r;
}
);
DTBase<string> guessF = new DTFunc<string>(delegate() { return Console.ReadLine(); });
DTFunc<string, bool> correctF = new DTFunc<string, bool>(a => { return !a.Call.Contains('X'); });
DTVar<string> number = numberF.Call;
int count = 20;
do
{
DTVar<string> guess = guessF.Call;
matchingF.Args1.AddRange = number.Call;
matchingF.Args2.AddRange = (string)guess;
DTVar<string> result = (string)matchingF;
correctF.Args.Add = result;
allCorrect = correctF.Call;
Console.WriteLine(result.Call);
} while (!allCorrect && --count >= 0);
Console.WriteLine("You {0}", (allCorrect ? "Win! :)" : "Loose. :("));
|
|
|
|
|
Hi Narf,
I think this kind of experimentation is a very cool thing to do: at a minimum you will come to a greater understanding of what the language limits are, and your creativity is definitely showing here !
I don't have the 'bandwidth' or inclination to study your code and figure out what you are doing, but it seems to me that you are moving C# one step past 'var' and 'dynamic' towards behaving like more loosely-typed languages (Haskell ? Ruby ?).
For me there's so much built-in to .NET in its current highly-evolved state, that I am going to stick with it, as is ... and it's evolving rapidly.
If I want a List of integers converted to a string, (as early as .NET 2.0, I believe), I can use Linq like this: (and there may well be a simpler way: I'm no expert on Linq):
List<string> s = new List<int> {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}.ConvertAll(i => Convert.ToString(i));
So, please do, keep going, follow your vision ! Don't let those who 'don't get it' phase you in the least
best, Bill
"In the River of Delights, Panic has not failed me." Jorge Luis Borges
modified on Monday, August 29, 2011 11:34 PM
|
|
|
|
|
I think there is an interesting discussion to be had here but I don't quite see what you are gaining over a Func<double> (with DTFunc<T>), or just a normal variable with DTVar.
|
|
|
|
|
I apologize if you did read this particular post, but it sounds like you didn't, so I'm reposting it.
<quote>
Valid in current paradigm:
var a = 5.5;
Invalid in current paradigm, but valid if paradigm theoretically adopted by C#:
a = new func<double>(delegate() { return 5.5; });
Using a theoretical library of additional functions, both are valid:
DTBase<double> a = (DTVar<double>)5.5;
a = new DTFunc<double>(delegate() { return 5.5; });
The key point is that, instead of being a specific type of variable, "a" is now defined by what you get when you call it.
If you're wondering what use being able to type variables by what they return when "called" is, a few suggested thoughts are:
Dictionary<string, DTBase<double>> formulasForGame;
List<DTBase<string>> urlRetrieval;
List<DTFunc<string, string>> chatRelays;
And any other situation where you want a generic, quick way to access data by type, not implementation. That is to say, counting double and Func(double) as separate implementations, but the same type.
Thank you for your patience and interest.
|
|
|
|
|
This:
a = new func<double>(delegate() { return 5.5; });
... may be invalid if you declare a as double, but if you want this kind of flexibility, surely you can use Func<double> in most cases (and when you want a constant, write a lambda or anonymous delegate to return it, as here).
I guess that is possibly useful in some cases though.
What is the performance hit like on this? You are adding a lookup (or method call) each time the 'variable' is referenced?
Have you considered:
public implicit operator T(DTBase<T> val) { return val.Value; }
public implicit operator DTBase<T>(T val) { return new DTVar<T>(val); }
That would make your implementation almost transparent.
|
|
|
|
|
are you suggesting implicit casting of Func<t> instances to T? or perhaps 'implicit resolving' would be a better term.
|
|
|
|
|
Implicitly storing anything that returns T when "called", in T.
|
|
|
|
|
I believe that is simply alternative semantics to what i said but its getting late now and the caffeine is wearing off so i can't be sure ..... my brain hurts
|
|
|
|
|
Func<t> is only one thing that could be placed in T. A Queue<t> would be equally qualified, as it has a candidate for calling that results in T.
|
|
|
|
|
Hello experts,
I have an inheritance issue that can be better explained in code than in text:
public abstract class AbstractBase
{
protected abstract void DoSomething();
}
public class FirstDerived : AbstractBase
{
protected override void DoSomething()
{
int foo = 5;
}
}
public class SecondDerived : AbstractBase
{
protected override void DoSomething()
{
int bar = 3;
}
private void DoAnotherThing()
{
FirstDerived firstDerived = new FirstDerived();
firstDerived.DoSomething();
}
}
The mentioned error in German reads
Auf den geschützten Member "AbstractBase.DoSomething()" kann nicht über einen Qualifizierer vom Typ "FirstDerived" zugegriffen werden. Der Qualifizierer muss vom Typ "SecondDerived" (oder von ihm abgeleitet) sein.
In English that would mean something like
A qualifier of type "FirstDerived" cannot access the protected member "AbstractBase.DoSomething()". The qualifier has to be of type "SecondDerived" (or derived thereof).
Why would that be?
Do I have to change from protected to internal ?
Ciao,
luker
|
|
|
|
|
Nothing to do with abstract, but the visibility of DoSomething(). It's protected meaning only visible to derived classes. You are trying to call it from outside, therefore it would need to be public.
Regards,
Rob Philpott.
|
|
|
|
|
Though I daresay you could use:
AbstractBase firstDerived = new FirstDerived();
firstDerived.DoSomething();
Now, because you can* access DoSomething in abstract base and because the function is virtual, the call will end up at FirstDerived::DoSomething.
However, this structure is quite odd and you should adjust your design a bit rather than hacking around it.
*) Actually, I'm not 100% sure, but I think this works
|
|
|
|
|
|
It's odd and should be redesigned because it breaks encapsulation. That function is protected for a reason. The code is creating a new object that is different from itself and then tries to call protected members - that is obviously an error. They share the same base class so it can bypass this restriction via a sort of cheat.
However, the idea behind the encapsulation / protected was that external sources do NOT have access to that function, and there must have been a reason why this is so. The fact that they share a base does not make it right (in terms of Object Oriented design) to bypass the rules.
Derived classes can access protected base members to support derivation, but a side-effect of this rule is that they can indirectly access protected members of classes they have no valid access to.
Like it has been suggested, if that function needs to be called, make it public, or internal.
|
|
|
|
|
|
Mark Nischalke wrote: However, I disagree that it is odd
Certainly looks wrong to me. Circumventing access on purpose mostly indicates a design problem. Doing it accidentally if of course an accident.
Mark Nischalke wrote: It's not an error
The compiler would seem to disagree with you.
|
|
|
|
|
Mark Nischalke wrote: Don't you think this would have been a better response to the OP than "this structure is quite odd and you should adjust your design"? How would they have know what odd meant or how it should be redesigned in your opinion?
You're just sulking because you disagree.
At any rate, maybe it'd be a better response to the OP if I explicitly explained the meaning of every word that is over 6 letters; after all, how are they supposed to know all those difficult words?
Why don't I? This has to do with estimating someone's level of knowledge and adjusting your answer in response. Obviously, I have a higher estimate of the OP's knowledge in this than you do. In the same way that I don't give the definition of every English word that is over 6 letters, I don't give explanation of what the word 'odd' means in this context (it was logical to me). About adjusting the design, well... like you said yourself, it's an example the OP gave, so I can't say anything relevant about that (at least, not that hadn't already been said by others). I'd need to know the actual design, and even then there might be multiple 'good' designs to go from.
Secondly, I refrained from a more elaborate explanation, because I preferred to wait and see if the OP is at all interested, before I waste time writing pages of info no one is ever going to read or care about. I find that people who are interested usually either understand it, or ask for explanation. If they're not interested, then there's no point in typing more than a succinct response in the first place.
Either way, you're very belittling in your response. Rather than appreciating a decent response, you snap out like I offended you personally. I find it hard to understand what warrants such a reaction. Therefore, I might be less than courteous in my response here; you reap what you sow (goes for me too).
To a more insightful point:
Mark Nischalke wrote: You mean this line?
FirstDerived firstDerived = new FirstDerived();
Well, yes, of course it is different. It is being called in class SecondDerived. I don't see anything odd about that.
Well, for one thing, you missed the line that was odd, in the quote. At any rate, I will attempt to explain. Considering the following example, which is design-wise equivalent to the original:
class Base
{
protected abstract void DoSomething();
}
class Derived : Base
{
protected override void DoSomething() {...}
private void SomethingElse()
{
CompletelyDifferent otherObject = new CompletelyDifferent();
otherObject.SomeFunction();
}
}
class CompletelyDifferent
{
protected void SomeFunction() {...}
}
Now I hope you agree that the line with the Error is an error for a good reason. And that trying to circumvent this error in any way is odd (or at least, bad practice).
The equivalence between this and the case where the two classes share a common base, from a design point of view, comes from the fact that having a shared element in no way means they have private access to each other. So, in our discussion of design philosophy, we are as well talking about the original example as this one.
Consider, for instance, a base Stream class and then a derived FileStream and an InternetStream. Even though they share a common base (Stream), they are totally unrelated, as FileStream knows nothing of streaming from the internet and InternetStream knows nothing of files. It would be extremely odd if FileStream then could have privileged (protected) access to a method InternetStream, under the nomer 'but they share a common base'. They are completely different and thus should be treated as such.
Sure, the 'hack' might allow it (not entirely sure that it does, but we're saying it does for the discussion), but it is a still a violation of the principles of encapsulation. It's protected for a reason.
Now of course, a fix to the above example would be to make SomeFunction public (or internal) so it can be properly accessed, but whether this is appropriate depends on the actual situation.
Mark Nischalke wrote: It's not an error. It is someone trying to learn, giving an example and asking for an explaination.
I was, of course, referring to the circumvention of the protected access specifier, which is an error.
|
|
|
|
|
|
Quite honestly Mark having read this thread now I think this is completely unwarranted and I've given you a 1 vote in consequence.
If instead of saying "Please elaborate why you think it odd and how it should be redesigned." you'd said something like "I'm not sure I agree. Could you elaborate on why you think it odd and how it might be redesigned?" you might have headed off the entire confrontation.
Anna
Tech Blog | Visual Lint
"Why would anyone prefer to wield a weapon that takes both hands at once, when they could use a lighter (and obviously superior) weapon that allows you to wield multiple ones at a time, and thus supports multi-paradigm carnage?"
|
|
|
|
|
I agree that Mark is succinct, perhaps even brusque, but he did not write a single uncivil or rude word. He asked for information, and he pointed out that one response was better than another at achieving that goal.
From my perspective, if one of my clients had told me what Mark did, I would try to be brief and succinct in my answer as well.
Come on guys, have some coffee and chill.
|
|
|
|
|
|
I was not offended that he asked for explanation in his first post; he was sort of right - I was being (deliberately) short there.
I just felt stabbed in the back for being talked to in a condescending way when I did post the information. And it was not what he said, but how he said it. The general tone matters a lot. If someone asks if you could get them a drink by "Could you get me a drink?" then you most likely will, but at any rate you're not at all offended. Asking "Get me a drink" holds the same request, but stated rudely. There is no uncivil or rude word in that sentence, but I do think I should have a right to be offended.
In the same way, the way his post was stated offended me.
An important aspect of a community like this, to me, is the atmosphere of learning and tolerance. I usually try my best to preserve and honor that.
And it did indeed feel to me (as also noted in the other post) that Mark was pulling ranks on me. I'm bad when it comes to taking that
I do feel like we (me and Mark) might have both been had by the language barrier. It's quite possible (and I am hoping) that he did not mean to be so brisk as he sounded to me.
At any rate, I've supplied CP with enough forum-drama for a while
|
|
|
|
|
Mark Nischalke wrote: Yours is not the attitude we want here on CP. If you can't comment politely or don't have the time to adequate respond then please don't visit here.
Like I said, you reap what you sow. And this goes both way.
You were plainly being rude in being condescending towards me and my explanation. Under some guise of decency you ask me to elaborate, and then when I do you make an under-the-belt stab.
So please, do also read that nice thread you linked and see if you understand it yourself.
If you truly question my attitude, then I suggest you look up some of my other responses and make your judgement then. I am well aware of proper etiquette. I just don't like being flamed for my effort.
If you have a problem with my explanation, then tell me in a normal way, and we can discuss. As I've tried to, between all these flame-war posts. I explained my reasoning and am always well open to comments and critics.
|
|
|
|
|
Don't worry. I did not in any way feel offended by any one of your posts.
And even now, re-reading them, I can't find why I should have.
Ciao,
luker
|
|
|
|
|